From aa06920e544df81186c8652b2cd9067f5542cff3 Mon Sep 17 00:00:00 2001 From: Ahmed Kadhim Date: Mon, 4 Nov 2024 10:55:03 +0000 Subject: [PATCH 01/33] add recom sys --- .gitignore | 3 + .../Applications/RecommendationSystems.py | 60 +++++++++++++++++++ examples/MNISTConvolutionDemo.py | 13 ---- examples/MNISTVanillaDemo.py | 15 ----- examples/NoisyXORDemo.py | 21 +------ examples/SequenceClassificationDemo.py | 14 ----- 6 files changed, 65 insertions(+), 61 deletions(-) create mode 100644 .gitignore create mode 100644 examples/Applications/RecommendationSystems.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..b4f6c429 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +build/ +GraphTsetlinMachine.egg-info/ +/dist/ \ No newline at end of file diff --git a/examples/Applications/RecommendationSystems.py b/examples/Applications/RecommendationSystems.py new file mode 100644 index 00000000..56b73b2e --- /dev/null +++ b/examples/Applications/RecommendationSystems.py @@ -0,0 +1,60 @@ +from GraphTsetlinMachine.graphs import Graphs +import numpy as np +from scipy.sparse import csr_matrix +from GraphTsetlinMachine.tm import MultiClassGraphTsetlinMachine +from time import time +import argparse +import random +import pandas as pd +import kagglehub + +def default_args(**kwargs): + parser = argparse.ArgumentParser() + parser.add_argument("--epochs", default=10, type=int) + parser.add_argument("--number-of-clauses", default=10, type=int) + parser.add_argument("--T", default=100, type=int) + parser.add_argument("--s", default=1.0, type=float) + parser.add_argument("--number-of-state-bits", default=8, type=int) + parser.add_argument("--depth", default=2, type=int) + parser.add_argument("--hypervector-size", default=32, type=int) + parser.add_argument("--hypervector-bits", default=2, type=int) + parser.add_argument("--message-size", default=256, type=int) + parser.add_argument("--message-bits", default=2, type=int) + parser.add_argument('--double-hashing', dest='double_hashing', default=False, action='store_true') + parser.add_argument("--noise", default=0.01, type=float) + parser.add_argument("--number-of-examples", default=10000, type=int) + parser.add_argument("--max-included-literals", default=4, type=int) + + args = parser.parse_args() + for key, value in kwargs.items(): + if key in args.__dict__: + setattr(args, key, value) + return args + +args = default_args() + +print("Creating training data") +path = kagglehub.dataset_download("arhamrumi/amazon-product-reviews") +print("Path to dataset files:", path) +data_file = path + "/Reviews.csv" # Adjust this path if necessary +data = pd.read_csv(data_file) +print("Data preview:", data.head()) + +number_of_nodes = 3 + +symbols = [] +users = data['user_id'].unique() +items = data['product_id'].unique() +categories = data['category'].unique() + +# Initialize Graphs with symbols for GTM +num_graphs = len(items) +symbols = ["I" + str(i) for i in items] + ["C" + str(c) for c in categories] + ["U" + str(u) for u in users] + +graphs_train = Graphs( + X_train.shape[0], + symbols=symbols, + hypervector_size=args.hypervector_size, + hypervector_bits=args.hypervector_bits, + double_hashing = args.double_hashing +) \ No newline at end of file diff --git a/examples/MNISTConvolutionDemo.py b/examples/MNISTConvolutionDemo.py index 8fe75473..a9ee5838 100644 --- a/examples/MNISTConvolutionDemo.py +++ b/examples/MNISTConvolutionDemo.py @@ -61,18 +61,13 @@ def default_args(**kwargs): hypervector_bits=args.hypervector_bits, double_hashing = args.double_hashing ) - for graph_id in range(X_train.shape[0]): graphs_train.set_number_of_graph_nodes(graph_id, number_of_nodes) - graphs_train.prepare_node_configuration() - for graph_id in range(X_train.shape[0]): for node_id in range(graphs_train.number_of_graph_nodes[graph_id]): graphs_train.add_graph_node(graph_id, node_id, 0) - graphs_train.prepare_edge_configuration() - for graph_id in range(X_train.shape[0]): if graph_id % 1000 == 0: print(graph_id, X_train.shape[0]) @@ -88,23 +83,17 @@ def default_args(**kwargs): graphs_train.add_graph_node_property(graph_id, node_id, "C:%d" % (q)) graphs_train.add_graph_node_property(graph_id, node_id, "R:%d" % (r)) - graphs_train.encode() - print("Training data produced") graphs_test = Graphs(X_test.shape[0], init_with=graphs_train) for graph_id in range(X_test.shape[0]): graphs_test.set_number_of_graph_nodes(graph_id, number_of_nodes) - graphs_test.prepare_node_configuration() - for graph_id in range(X_test.shape[0]): for node_id in range(graphs_test.number_of_graph_nodes[graph_id]): graphs_test.add_graph_node(graph_id, node_id, 0) - graphs_test.prepare_edge_configuration() - for graph_id in range(X_test.shape[0]): if graph_id % 1000 == 0: print(graph_id, X_test.shape[0]) @@ -120,9 +109,7 @@ def default_args(**kwargs): graphs_test.add_graph_node_property(graph_id, node_id, "C:%d" % (q)) graphs_test.add_graph_node_property(graph_id, node_id, "R:%d" % (r)) - graphs_test.encode() - print("Testing data produced") tm = MultiClassGraphTsetlinMachine( diff --git a/examples/MNISTVanillaDemo.py b/examples/MNISTVanillaDemo.py index 8bcb453c..02b95e2a 100644 --- a/examples/MNISTVanillaDemo.py +++ b/examples/MNISTVanillaDemo.py @@ -4,7 +4,6 @@ from GraphTsetlinMachine.tm import MultiClassGraphTsetlinMachine from time import time import argparse -from skimage.util import view_as_windows from keras.datasets import mnist from numba import jit @@ -53,51 +52,37 @@ def default_args(**kwargs): hypervector_bits=args.hypervector_bits, double_hashing = args.double_hashing ) - for graph_id in range(X_train.shape[0]): graphs_train.set_number_of_graph_nodes(graph_id, number_of_nodes) - graphs_train.prepare_node_configuration() - for graph_id in range(X_train.shape[0]): number_of_outgoing_edges = 0 graphs_train.add_graph_node(graph_id, 'Image Node', number_of_outgoing_edges) - graphs_train.prepare_edge_configuration() - for graph_id in range(X_train.shape[0]): if graph_id % 1000 == 0: print(graph_id, X_train.shape[0]) for k in X_train[graph_id].nonzero()[0]: graphs_train.add_graph_node_property(graph_id, 'Image Node', "W%d,%d" % (k // 28, k % 28)) - graphs_train.encode() - print("Training data produced") graphs_test = Graphs(X_test.shape[0], init_with=graphs_train) - for graph_id in range(X_test.shape[0]): graphs_test.set_number_of_graph_nodes(graph_id, number_of_nodes) - graphs_test.prepare_node_configuration() - for graph_id in range(X_test.shape[0]): number_of_outgoing_edges = 0 graphs_test.add_graph_node(graph_id, 'Image Node', number_of_outgoing_edges) - graphs_test.prepare_edge_configuration() - for graph_id in range(X_test.shape[0]): if graph_id % 1000 == 0: print(graph_id, X_test.shape[0]) for k in X_test[graph_id].nonzero()[0]: graphs_test.add_graph_node_property(graph_id, 'Image Node', "W%d,%d" % (k // 28, k % 28)) - graphs_test.encode() - print("Testing data produced") tm = MultiClassGraphTsetlinMachine( diff --git a/examples/NoisyXORDemo.py b/examples/NoisyXORDemo.py index 83a4bbde..3069207d 100644 --- a/examples/NoisyXORDemo.py +++ b/examples/NoisyXORDemo.py @@ -34,31 +34,24 @@ def default_args(**kwargs): print("Creating training data") # Create train data - graphs_train = Graphs( args.number_of_examples, symbols=['A', 'B'], hypervector_size=args.hypervector_size, hypervector_bits=args.hypervector_bits, ) - for graph_id in range(args.number_of_examples): graphs_train.set_number_of_graph_nodes(graph_id, 2) - graphs_train.prepare_node_configuration() - for graph_id in range(args.number_of_examples): number_of_outgoing_edges = 1 graphs_train.add_graph_node(graph_id, 'Node 1', number_of_outgoing_edges) graphs_train.add_graph_node(graph_id, 'Node 2', number_of_outgoing_edges) - -graphs_train.prepare_edge_configuration() - +graphs_train.prepar_eedge_configuration() for graph_id in range(args.number_of_examples): edge_type = "Plain" graphs_train.add_graph_node_edge(graph_id, 'Node 1', 'Node 2', edge_type) graphs_train.add_graph_node_edge(graph_id, 'Node 2', 'Node 1', edge_type) - Y_train = np.empty(args.number_of_examples, dtype=np.uint32) for graph_id in range(args.number_of_examples): x1 = random.choice(['A', 'B']) @@ -74,32 +67,23 @@ def default_args(**kwargs): if np.random.rand() <= args.noise: Y_train[graph_id] = 1 - Y_train[graph_id] - graphs_train.encode() -# Create test data - +# Create test data print("Creating testing data") - graphs_test = Graphs(args.number_of_examples, init_with=graphs_train) - for graph_id in range(args.number_of_examples): graphs_test.set_number_of_graph_nodes(graph_id, 2) - graphs_test.prepare_node_configuration() - for graph_id in range(args.number_of_examples): number_of_outgoing_edges = 1 graphs_test.add_graph_node(graph_id, 'Node 1', number_of_outgoing_edges) graphs_test.add_graph_node(graph_id, 'Node 2', number_of_outgoing_edges) - graphs_test.prepare_edge_configuration() - for graph_id in range(args.number_of_examples): edge_type = "Plain" graphs_test.add_graph_node_edge(graph_id, 'Node 1', 'Node 2', edge_type) graphs_test.add_graph_node_edge(graph_id, 'Node 2', 'Node 1', edge_type) - Y_test = np.empty(args.number_of_examples, dtype=np.uint32) for graph_id in range(args.number_of_examples): x1 = random.choice(['A', 'B']) @@ -112,7 +96,6 @@ def default_args(**kwargs): Y_test[graph_id] = 0 else: Y_test[graph_id] = 1 - graphs_test.encode() tm = MultiClassGraphTsetlinMachine( diff --git a/examples/SequenceClassificationDemo.py b/examples/SequenceClassificationDemo.py index 7a2362cb..c5b13214 100644 --- a/examples/SequenceClassificationDemo.py +++ b/examples/SequenceClassificationDemo.py @@ -35,7 +35,6 @@ def default_args(**kwargs): print("Creating training data") # Create train data - graphs_train = Graphs( args.number_of_examples, symbols=['A'], @@ -43,19 +42,14 @@ def default_args(**kwargs): hypervector_bits=args.hypervector_bits, double_hashing = args.double_hashing ) - for graph_id in range(args.number_of_examples): graphs_train.set_number_of_graph_nodes(graph_id, np.random.randint(args.number_of_classes, args.max_sequence_length+1)) - graphs_train.prepare_node_configuration() - for graph_id in range(args.number_of_examples): for node_id in range(graphs_train.number_of_graph_nodes[graph_id]): number_of_edges = 2 if node_id > 0 and node_id < graphs_train.number_of_graph_nodes[graph_id]-1 else 1 graphs_train.add_graph_node(graph_id, node_id, number_of_edges) - graphs_train.prepare_edge_configuration() - Y_train = np.empty(args.number_of_examples, dtype=np.uint32) for graph_id in range(args.number_of_examples): for node_id in range(graphs_train.number_of_graph_nodes[graph_id]): @@ -76,26 +70,19 @@ def default_args(**kwargs): if np.random.rand() <= args.noise: Y_train[graph_id] = np.random.choice(np.setdiff1d(np.arange(args.number_of_classes), [Y_train[graph_id]])) - graphs_train.encode() # Create test data - print("Creating testing data") - graphs_test = Graphs(args.number_of_examples, init_with=graphs_train) for graph_id in range(args.number_of_examples): graphs_test.set_number_of_graph_nodes(graph_id, np.random.randint(args.number_of_classes, args.max_sequence_length+1)) - graphs_test.prepare_node_configuration() - for graph_id in range(args.number_of_examples): for node_id in range(graphs_test.number_of_graph_nodes[graph_id]): number_of_edges = 2 if node_id > 0 and node_id < graphs_test.number_of_graph_nodes[graph_id]-1 else 1 graphs_test.add_graph_node(graph_id, node_id, number_of_edges) - graphs_test.prepare_edge_configuration() - Y_test = np.empty(args.number_of_examples, dtype=np.uint32) for graph_id in range(args.number_of_examples): for node_id in range(graphs_test.number_of_graph_nodes[graph_id]): @@ -113,7 +100,6 @@ def default_args(**kwargs): node_id = np.random.randint(Y_test[graph_id], graphs_test.number_of_graph_nodes[graph_id]) for node_pos in range(Y_test[graph_id] + 1): graphs_test.add_graph_node_property(graph_id, node_id - node_pos, 'A') - graphs_test.encode() tm = MultiClassGraphTsetlinMachine( From 6280bfbc95ab1f3a2ce80ccde68b16851293dbb6 Mon Sep 17 00:00:00 2001 From: Ahmed Kadhim Date: Mon, 4 Nov 2024 10:58:20 +0000 Subject: [PATCH 02/33] rename --- examples/{Applications => applications}/RecommendationSystems.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename examples/{Applications => applications}/RecommendationSystems.py (100%) diff --git a/examples/Applications/RecommendationSystems.py b/examples/applications/RecommendationSystems.py similarity index 100% rename from examples/Applications/RecommendationSystems.py rename to examples/applications/RecommendationSystems.py From 771edcf2af1bd1b4bb97606adcddbda472cdae0c Mon Sep 17 00:00:00 2001 From: Ahmed Kadhim Date: Wed, 6 Nov 2024 08:49:04 +0000 Subject: [PATCH 03/33] complete recom sys --- examples/MNISTVanillaDemo.py | 31 ++-- examples/NoisyXORMNISTDemo.py | 16 -- .../applications/RecommendationSystems.py | 165 ++++++++++++++++-- examples/applications/test.ipynb | 101 +++++++++++ 4 files changed, 264 insertions(+), 49 deletions(-) create mode 100644 examples/applications/test.ipynb diff --git a/examples/MNISTVanillaDemo.py b/examples/MNISTVanillaDemo.py index 02b95e2a..4428343f 100644 --- a/examples/MNISTVanillaDemo.py +++ b/examples/MNISTVanillaDemo.py @@ -60,9 +60,8 @@ def default_args(**kwargs): graphs_train.add_graph_node(graph_id, 'Image Node', number_of_outgoing_edges) graphs_train.prepare_edge_configuration() for graph_id in range(X_train.shape[0]): - if graph_id % 1000 == 0: - print(graph_id, X_train.shape[0]) - + # if graph_id % 1000 == 0: + # print(graph_id, X_train.shape[0]) for k in X_train[graph_id].nonzero()[0]: graphs_train.add_graph_node_property(graph_id, 'Image Node', "W%d,%d" % (k // 28, k % 28)) graphs_train.encode() @@ -110,16 +109,16 @@ def default_args(**kwargs): print("%d %.2f %.2f %.2f %.2f" % (i, result_train, result_test, stop_training-start_training, stop_testing-start_testing)) -weights = tm.get_state()[1].reshape(2, -1) -for i in range(tm.number_of_clauses): - print("Clause #%d Weights:(%d %d)" % (i, weights[0,i], weights[1,i]), end=' ') - l = [] - for k in range(args.hypervector_size * 2): - if tm.ta_action(0, i, k): - if k < args.hypervector_size: - l.append("x%d" % (k)) - else: - l.append("NOT x%d" % (k - args.hypervector_size)) - print(" AND ".join(l)) - -print(graphs_train.hypervectors) \ No newline at end of file +# weights = tm.get_state()[1].reshape(2, -1) +# for i in range(tm.number_of_clauses): +# print("Clause #%d Weights:(%d %d)" % (i, weights[0,i], weights[1,i]), end=' ') +# l = [] +# for k in range(args.hypervector_size * 2): +# if tm.ta_action(0, i, k): +# if k < args.hypervector_size: +# l.append("x%d" % (k)) +# else: +# l.append("NOT x%d" % (k - args.hypervector_size)) +# print(" AND ".join(l)) + +# print(graphs_train.hypervectors) \ No newline at end of file diff --git a/examples/NoisyXORMNISTDemo.py b/examples/NoisyXORMNISTDemo.py index ff1b3151..5da47877 100644 --- a/examples/NoisyXORMNISTDemo.py +++ b/examples/NoisyXORMNISTDemo.py @@ -54,24 +54,18 @@ def default_args(**kwargs): hypervector_size=args.hypervector_size, hypervector_bits=args.hypervector_bits, ) - for graph_id in range(args.number_of_examples): graphs_train.set_number_of_graph_nodes(graph_id, 2) - graphs_train.prepare_node_configuration() - for graph_id in range(args.number_of_examples): number_of_outgoing_edges = 1 graphs_train.add_graph_node(graph_id, 'Node 1', number_of_outgoing_edges) graphs_train.add_graph_node(graph_id, 'Node 2', number_of_outgoing_edges) - graphs_train.prepare_edge_configuration() - for graph_id in range(args.number_of_examples): edge_type = "Plain" graphs_train.add_graph_node_edge(graph_id, 'Node 1', 'Node 2', edge_type) graphs_train.add_graph_node_edge(graph_id, 'Node 2', 'Node 1', edge_type) - Y_train = np.empty(args.number_of_examples, dtype=np.uint32) for graph_id in range(args.number_of_examples): x1 = random.choice([0, 1]) @@ -91,32 +85,23 @@ def default_args(**kwargs): if np.random.rand() <= args.noise: Y_train[graph_id] = 1 - Y_train[graph_id] - graphs_train.encode() # Create test data - print("Creating testing data") - graphs_test = Graphs(args.number_of_examples, init_with=graphs_train) - for graph_id in range(args.number_of_examples): graphs_test.set_number_of_graph_nodes(graph_id, 2) - graphs_test.prepare_node_configuration() - for graph_id in range(args.number_of_examples): number_of_outgoing_edges = 1 graphs_test.add_graph_node(graph_id, 'Node 1', number_of_outgoing_edges) graphs_test.add_graph_node(graph_id, 'Node 2', number_of_outgoing_edges) - graphs_test.prepare_edge_configuration() - for graph_id in range(args.number_of_examples): edge_type = "Plain" graphs_test.add_graph_node_edge(graph_id, 'Node 1', 'Node 2', edge_type) graphs_test.add_graph_node_edge(graph_id, 'Node 2', 'Node 1', edge_type) - Y_test = np.empty(args.number_of_examples, dtype=np.uint32) for graph_id in range(args.number_of_examples): x1 = random.choice([0, 1]) @@ -133,7 +118,6 @@ def default_args(**kwargs): Y_test[graph_id] = 0 else: Y_test[graph_id] = 1 - graphs_test.encode() tm = MultiClassGraphTsetlinMachine( diff --git a/examples/applications/RecommendationSystems.py b/examples/applications/RecommendationSystems.py index 56b73b2e..8901911c 100644 --- a/examples/applications/RecommendationSystems.py +++ b/examples/applications/RecommendationSystems.py @@ -1,25 +1,24 @@ from GraphTsetlinMachine.graphs import Graphs -import numpy as np -from scipy.sparse import csr_matrix from GraphTsetlinMachine.tm import MultiClassGraphTsetlinMachine from time import time import argparse -import random import pandas as pd import kagglehub +from sklearn.model_selection import train_test_split +from sklearn.preprocessing import LabelEncoder def default_args(**kwargs): parser = argparse.ArgumentParser() - parser.add_argument("--epochs", default=10, type=int) - parser.add_argument("--number-of-clauses", default=10, type=int) + parser.add_argument("--epochs", default=250, type=int) + parser.add_argument("--number-of-clauses", default=60, type=int) parser.add_argument("--T", default=100, type=int) - parser.add_argument("--s", default=1.0, type=float) + parser.add_argument("--s", default=10.0, type=float) parser.add_argument("--number-of-state-bits", default=8, type=int) parser.add_argument("--depth", default=2, type=int) - parser.add_argument("--hypervector-size", default=32, type=int) - parser.add_argument("--hypervector-bits", default=2, type=int) + parser.add_argument("--hypervector-size", default=1024, type=int) + parser.add_argument("--hypervector-bits", default=8, type=int) parser.add_argument("--message-size", default=256, type=int) - parser.add_argument("--message-bits", default=2, type=int) + parser.add_argument("--message-bits", default=8, type=int) parser.add_argument('--double-hashing', dest='double_hashing', default=False, action='store_true') parser.add_argument("--noise", default=0.01, type=float) parser.add_argument("--number-of-examples", default=10000, type=int) @@ -34,27 +33,159 @@ def default_args(**kwargs): args = default_args() print("Creating training data") -path = kagglehub.dataset_download("arhamrumi/amazon-product-reviews") +path = kagglehub.dataset_download("karkavelrajaj/amazon-sales-dataset") print("Path to dataset files:", path) -data_file = path + "/Reviews.csv" # Adjust this path if necessary +data_file = path + "/amazon.csv" data = pd.read_csv(data_file) -print("Data preview:", data.head()) +# print("Data preview:", data.head()) +data = data[['product_id', 'category', 'user_id', 'rating']] + +le_user = LabelEncoder() +le_item = LabelEncoder() +le_category = LabelEncoder() +le_rating = LabelEncoder() -number_of_nodes = 3 +data['user_id'] = le_user.fit_transform(data['user_id']) +data['product_id'] = le_item.fit_transform(data['product_id']) +data['category'] = le_category.fit_transform(data['category']) +data['rating'] = le_rating.fit_transform(data['rating']) + +x = data[['user_id', 'product_id', 'category']].values +y = data['rating'].values + +X_train, X_test, Y_train, Y_test = train_test_split(x, y, test_size=0.2, random_state=42) + +print("X_train shape:", X_train.shape) +print("y_train shape:", Y_train.shape) +print("X_test shape:", X_test.shape) +print("y_test shape:", Y_test.shape) -symbols = [] users = data['user_id'].unique() items = data['product_id'].unique() categories = data['category'].unique() # Initialize Graphs with symbols for GTM -num_graphs = len(items) -symbols = ["I" + str(i) for i in items] + ["C" + str(c) for c in categories] + ["U" + str(u) for u in users] +number_of_nodes = 3 +symbols = [] +symbols = ["U_" + str(u) for u in users] + ["I_" + str(i) for i in items] + ["C_" + str(c) for c in categories] +# Train data graphs_train = Graphs( X_train.shape[0], symbols=symbols, hypervector_size=args.hypervector_size, hypervector_bits=args.hypervector_bits, double_hashing = args.double_hashing -) \ No newline at end of file +) +for graph_id in range(X_train.shape[0]): + graphs_train.set_number_of_graph_nodes(graph_id, number_of_nodes) +graphs_train.prepare_node_configuration() +for graph_id in range(X_train.shape[0]): + for node_id in range(graphs_train.number_of_graph_nodes[graph_id]): + number_of_edges = 2 if node_id > 0 and node_id < graphs_train.number_of_graph_nodes[graph_id]-1 else 1 + if node_id == 0: + graphs_train.add_graph_node(graph_id, "User", number_of_edges) + elif node_id == 1: + graphs_train.add_graph_node(graph_id, "Item", number_of_edges) + else: + graphs_train.add_graph_node(graph_id, "Category", number_of_edges) +graphs_train.prepare_edge_configuration() +for graph_id in range(X_train.shape[0]): + for node_id in range(graphs_train.number_of_graph_nodes[graph_id]): + if node_id == 0: + graphs_train.add_graph_node_edge(graph_id, "User", "Item", "UserItem") + + if node_id == 1: + graphs_train.add_graph_node_edge(graph_id, "Item", "Category", "ItemCategory") + graphs_train.add_graph_node_edge(graph_id, "Item", "User", "ItemUser") + + if node_id == 2: + graphs_train.add_graph_node_edge(graph_id, "Category", "Item", "CatrgoryItem") + + graphs_train.add_graph_node_property(graph_id, "User", "U_" + str(X_train[graph_id][0])) + graphs_train.add_graph_node_property(graph_id, "Item", "I_" + str(X_train[graph_id][1])) + graphs_train.add_graph_node_property(graph_id, "Category", "C_" + str(X_train[graph_id][2])) +graphs_train.encode() +print("Training data produced") + +# Test data +graphs_test = Graphs(X_test.shape[0], init_with=graphs_train) +for graph_id in range(X_test.shape[0]): + graphs_test.set_number_of_graph_nodes(graph_id, number_of_nodes) +graphs_test.prepare_node_configuration() +for graph_id in range(X_test.shape[0]): + for node_id in range(graphs_test.number_of_graph_nodes[graph_id]): + number_of_edges = 2 if node_id > 0 and node_id < graphs_test.number_of_graph_nodes[graph_id]-1 else 1 + if node_id == 0: + graphs_test.add_graph_node(graph_id, "User", number_of_edges) + elif node_id == 1: + graphs_test.add_graph_node(graph_id, "Item", number_of_edges) + else: + graphs_test.add_graph_node(graph_id, "Category", number_of_edges) +graphs_test.prepare_edge_configuration() +for graph_id in range(X_test.shape[0]): + for node_id in range(graphs_test.number_of_graph_nodes[graph_id]): + if node_id == 0: + graphs_test.add_graph_node_edge(graph_id, "User", "Item", "UserItem") + + if node_id == 1: + graphs_test.add_graph_node_edge(graph_id, "Item", "Category", "ItemCategory") + graphs_test.add_graph_node_edge(graph_id, "Item", "User", "ItemUser") + + if node_id == 2: + graphs_test.add_graph_node_edge(graph_id, "Category", "Item", "CatrgoryItem") + + graphs_test.add_graph_node_property(graph_id, "User", "U_" + str(X_test[graph_id][0])) + graphs_test.add_graph_node_property(graph_id, "Item", "I_" + str(X_test[graph_id][1])) + graphs_test.add_graph_node_property(graph_id, "Category", "C_" + str(X_test[graph_id][2])) +graphs_test.encode() +print("Testing data produced") + +tm = MultiClassGraphTsetlinMachine( + args.number_of_clauses, + args.T, + args.s, + number_of_state_bits = args.number_of_state_bits, + depth=args.depth, + message_size=args.message_size, + message_bits=args.message_bits, + max_included_literals=args.max_included_literals, + double_hashing = args.double_hashing +) + +for i in range(args.epochs): + start_training = time() + tm.fit(graphs_train, Y_train, epochs=1, incremental=True) + stop_training = time() + + start_testing = time() + result_test = 100*(tm.predict(graphs_test) == Y_test).mean() + stop_testing = time() + + result_train = 100*(tm.predict(graphs_train) == Y_train).mean() + + print("%d %.2f %.2f %.2f %.2f" % (i, result_train, result_test, stop_training-start_training, stop_testing-start_testing)) + +# weights = tm.get_state()[1].reshape(2, -1) +# for i in range(tm.number_of_clauses): +# print("Clause #%d W:(%d %d)" % (i, weights[0,i], weights[1,i]), end=' ') +# l = [] +# for k in range(args.hypervector_size * 2): +# if tm.ta_action(0, i, k): +# if k < args.hypervector_size: +# l.append("x%d" % (k)) +# else: +# l.append("NOT x%d" % (k - args.hypervector_size)) + +# for k in range(args.message_size * 2): +# if tm.ta_action(1, i, k): +# if k < args.message_size: +# l.append("c%d" % (k)) +# else: +# l.append("NOT c%d" % (k - args.message_size)) + +# print(" AND ".join(l)) + +# print(graphs_test.hypervectors) +# print(tm.hypervectors) +# print(graphs_test.edge_type_id) \ No newline at end of file diff --git a/examples/applications/test.ipynb b/examples/applications/test.ipynb new file mode 100644 index 00000000..44e02947 --- /dev/null +++ b/examples/applications/test.ipynb @@ -0,0 +1,101 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Creating training data\n", + "Path to dataset files: /root/.cache/kagglehub/datasets/karkavelrajaj/amazon-sales-dataset/versions/1\n", + "Electronics|HomeTheater,TV&Video|Accessories|RemoteControls\n", + "X_train shape: (1172, 3)\n", + "y_train shape: (1172,)\n", + "X_test shape: (293, 3)\n", + "y_test shape: (293,)\n", + "111\n", + "Electronics|HomeTheater,TV&Video|Accessories|RemoteControls\n" + ] + } + ], + "source": [ + "from GraphTsetlinMachine.graphs import Graphs\n", + "import numpy as np\n", + "from scipy.sparse import csr_matrix\n", + "from GraphTsetlinMachine.tm import MultiClassGraphTsetlinMachine\n", + "from time import time\n", + "import argparse\n", + "import random\n", + "import pandas as pd\n", + "import kagglehub\n", + "from sklearn.model_selection import train_test_split\n", + "from sklearn.preprocessing import LabelEncoder\n", + "\n", + "\n", + "print(\"Creating training data\")\n", + "path = kagglehub.dataset_download(\"karkavelrajaj/amazon-sales-dataset\")\n", + "print(\"Path to dataset files:\", path)\n", + "data_file = path + \"/amazon.csv\" # Adjust this path if necessary\n", + "data = pd.read_csv(data_file)\n", + "# print(\"Data preview:\", data.head())\n", + "data = data[['product_id', 'category', 'user_id', 'rating']]\n", + "print(data['category'][100])\n", + " \n", + "# Step 2: Encode user_id, product_id, and category with LabelEncoder\n", + "# This converts string identifiers into unique integer values\n", + "le_user = LabelEncoder()\n", + "le_item = LabelEncoder()\n", + "le_category = LabelEncoder()\n", + "\n", + "data['user_id'] = le_user.fit_transform(data['user_id'])\n", + "data['product_id'] = le_item.fit_transform(data['product_id'])\n", + "data['category'] = le_category.fit_transform(data['category'])\n", + "\n", + "# Step 3: Prepare X (features) and y (labels)\n", + "x = data[['user_id', 'product_id', 'category']].values # Features: [user, item, category]\n", + "y = data['rating'].values # Labels: rating\n", + "\n", + "# Step 4: Split the data into training and test sets\n", + "X_train, X_test, Y_train, Y_test = train_test_split(x, y, test_size=0.2, random_state=42)\n", + "\n", + "# Display the shapes to verify the split\n", + "print(\"X_train shape:\", X_train.shape)\n", + "print(\"y_train shape:\", Y_train.shape)\n", + "print(\"X_test shape:\", X_test.shape)\n", + "print(\"y_test shape:\", Y_test.shape)\n", + "\n", + "users = data['user_id'].unique()\n", + "items = data['product_id'].unique()\n", + "categories = data['category'].unique()\n", + "\n", + "print(categories[100])\n", + "original_user_id = le_category.inverse_transform([data['category'][100]])[0]\n", + "print(original_user_id)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} From ec3fc8725952d91e73fe17f0ad6a3628afa6ccd8 Mon Sep 17 00:00:00 2001 From: Ahmed Khalid Date: Wed, 4 Dec 2024 11:29:05 +0000 Subject: [PATCH 04/33] rename --- .devcontainer/devcontainer.json | 4 ++-- .devcontainer/docker-compose.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index e500cf24..b264ff4a 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,7 +1,7 @@ { - "name": "TM Graph Devcontainer", + "name": "TM Graph Recomm", "dockerComposeFile": "docker-compose.yml", - "service": "tm-graph-development", + "service": "tm-graph-recomm", "workspaceFolder": "/app", "forwardPorts": [], "postCreateCommand": "echo 'Devcontainer is ready'", diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml index 183c3acd..0dccd188 100644 --- a/.devcontainer/docker-compose.yml +++ b/.devcontainer/docker-compose.yml @@ -1,5 +1,5 @@ services: - tm-graph-development: + tm-graph-recomm: build: context: ../ dockerfile: .devcontainer/Dockerfile From 08693ab145312d82fe5e99bb04bb82a2e9a35194 Mon Sep 17 00:00:00 2001 From: Ahmed Khalid Date: Wed, 4 Dec 2024 13:22:41 +0000 Subject: [PATCH 05/33] tunning --- examples/applications/RecommendationSystems.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/examples/applications/RecommendationSystems.py b/examples/applications/RecommendationSystems.py index 8901911c..016b154f 100644 --- a/examples/applications/RecommendationSystems.py +++ b/examples/applications/RecommendationSystems.py @@ -10,18 +10,18 @@ def default_args(**kwargs): parser = argparse.ArgumentParser() parser.add_argument("--epochs", default=250, type=int) - parser.add_argument("--number-of-clauses", default=60, type=int) - parser.add_argument("--T", default=100, type=int) + parser.add_argument("--number-of-clauses", default=1000, type=int) + parser.add_argument("--T", default=1000, type=int) parser.add_argument("--s", default=10.0, type=float) parser.add_argument("--number-of-state-bits", default=8, type=int) - parser.add_argument("--depth", default=2, type=int) - parser.add_argument("--hypervector-size", default=1024, type=int) - parser.add_argument("--hypervector-bits", default=8, type=int) - parser.add_argument("--message-size", default=256, type=int) - parser.add_argument("--message-bits", default=8, type=int) + parser.add_argument("--depth", default=3, type=int) + parser.add_argument("--hypervector-size", default=16384, type=int) + parser.add_argument("--hypervector-bits", default=328, type=int) + parser.add_argument("--message-size", default=1024, type=int) + parser.add_argument("--message-bits", default=32, type=int) parser.add_argument('--double-hashing', dest='double_hashing', default=False, action='store_true') parser.add_argument("--noise", default=0.01, type=float) - parser.add_argument("--number-of-examples", default=10000, type=int) + parser.add_argument("--number-of-examples", default=1000, type=int) parser.add_argument("--max-included-literals", default=4, type=int) args = parser.parse_args() @@ -68,7 +68,7 @@ def default_args(**kwargs): number_of_nodes = 3 symbols = [] symbols = ["U_" + str(u) for u in users] + ["I_" + str(i) for i in items] + ["C_" + str(c) for c in categories] - +print(len(symbols)) # Train data graphs_train = Graphs( X_train.shape[0], From 9c4be1f888844ae37879a6fc97ff68561d4f62d2 Mon Sep 17 00:00:00 2001 From: Ahmed Khalid Date: Mon, 16 Dec 2024 12:19:18 +0000 Subject: [PATCH 06/33] update --- examples/applications/RecommendationSystems.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/applications/RecommendationSystems.py b/examples/applications/RecommendationSystems.py index 016b154f..4cb0751d 100644 --- a/examples/applications/RecommendationSystems.py +++ b/examples/applications/RecommendationSystems.py @@ -16,7 +16,7 @@ def default_args(**kwargs): parser.add_argument("--number-of-state-bits", default=8, type=int) parser.add_argument("--depth", default=3, type=int) parser.add_argument("--hypervector-size", default=16384, type=int) - parser.add_argument("--hypervector-bits", default=328, type=int) + parser.add_argument("--hypervector-bits", default=496, type=int) parser.add_argument("--message-size", default=1024, type=int) parser.add_argument("--message-bits", default=32, type=int) parser.add_argument('--double-hashing', dest='double_hashing', default=False, action='store_true') From daf8d5ad1f8319beafbe7d4c654bd1db02695a2c Mon Sep 17 00:00:00 2001 From: Ahmed Khalid Date: Tue, 17 Dec 2024 14:39:07 +0000 Subject: [PATCH 07/33] update --- .../applications/RecommendationSystems.py | 50 ++++++++++++++----- 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/examples/applications/RecommendationSystems.py b/examples/applications/RecommendationSystems.py index 4cb0751d..7e2cdeef 100644 --- a/examples/applications/RecommendationSystems.py +++ b/examples/applications/RecommendationSystems.py @@ -3,6 +3,7 @@ from time import time import argparse import pandas as pd +import numpy as np import kagglehub from sklearn.model_selection import train_test_split from sklearn.preprocessing import LabelEncoder @@ -15,13 +16,13 @@ def default_args(**kwargs): parser.add_argument("--s", default=10.0, type=float) parser.add_argument("--number-of-state-bits", default=8, type=int) parser.add_argument("--depth", default=3, type=int) - parser.add_argument("--hypervector-size", default=16384, type=int) - parser.add_argument("--hypervector-bits", default=496, type=int) - parser.add_argument("--message-size", default=1024, type=int) - parser.add_argument("--message-bits", default=32, type=int) + parser.add_argument("--hypervector-size", default=1024, type=int) + parser.add_argument("--hypervector-bits", default=10, type=int) + parser.add_argument("--message-size", default=512, type=int) + parser.add_argument("--message-bits", default=10, type=int) parser.add_argument('--double-hashing', dest='double_hashing', default=False, action='store_true') parser.add_argument("--noise", default=0.01, type=float) - parser.add_argument("--number-of-examples", default=1000, type=int) + parser.add_argument("--number-of-examples", default=500, type=int) parser.add_argument("--max-included-literals", default=4, type=int) args = parser.parse_args() @@ -32,13 +33,38 @@ def default_args(**kwargs): args = default_args() -print("Creating training data") -path = kagglehub.dataset_download("karkavelrajaj/amazon-sales-dataset") -print("Path to dataset files:", path) -data_file = path + "/amazon.csv" -data = pd.read_csv(data_file) -# print("Data preview:", data.head()) -data = data[['product_id', 'category', 'user_id', 'rating']] +# print("Creating training data") +# path = kagglehub.dataset_download("karkavelrajaj/amazon-sales-dataset") +# print("Path to dataset files:", path) +# data_file = path + "/amazon.csv" +# data = pd.read_csv(data_file) +# # print("Data preview:", data.head()) +# data = data[['product_id', 'category', 'user_id', 'rating']] + +############################# artificial dataset ######################## +# Set random seed for reproducibility +np.random.seed(42) +# Define the size of the artificial dataset +num_users = 10 # Number of unique users +num_items = 50 # Number of unique items +num_categories = 10 # Number of unique categories +num_interactions = 10000 # Number of user-item interactions +# Generate random ratings (e.g., between 1 and 5) +ratings = np.random.choice(range(1, 3), num_interactions) +# Generate random user-item interactions +user_ids = np.random.choice(range(num_users), num_interactions) +item_ids = np.random.choice(range(num_items), num_interactions) +categories = np.random.choice(range(num_categories), num_interactions) +# Combine into a DataFrame +data = pd.DataFrame({ + 'user_id': user_ids, + 'product_id': item_ids, + 'category': categories, + 'rating': ratings +}) +print("Artificial Dataset Preview:") +print(data.head()) +######################################################################## le_user = LabelEncoder() le_item = LabelEncoder() From 9dacba5364e8abc3f9c746399f8c5185d4410cad Mon Sep 17 00:00:00 2001 From: Ahmed Khalid Date: Wed, 18 Dec 2024 10:25:12 +0000 Subject: [PATCH 08/33] update --- examples/applications/RecommendationSystems.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/applications/RecommendationSystems.py b/examples/applications/RecommendationSystems.py index 7e2cdeef..a453d42d 100644 --- a/examples/applications/RecommendationSystems.py +++ b/examples/applications/RecommendationSystems.py @@ -19,7 +19,7 @@ def default_args(**kwargs): parser.add_argument("--hypervector-size", default=1024, type=int) parser.add_argument("--hypervector-bits", default=10, type=int) parser.add_argument("--message-size", default=512, type=int) - parser.add_argument("--message-bits", default=10, type=int) + parser.add_argument("--message-bits", default=2, type=int) parser.add_argument('--double-hashing', dest='double_hashing', default=False, action='store_true') parser.add_argument("--noise", default=0.01, type=float) parser.add_argument("--number-of-examples", default=500, type=int) @@ -48,7 +48,7 @@ def default_args(**kwargs): num_users = 10 # Number of unique users num_items = 50 # Number of unique items num_categories = 10 # Number of unique categories -num_interactions = 10000 # Number of user-item interactions +num_interactions = 100000 # Number of user-item interactions # Generate random ratings (e.g., between 1 and 5) ratings = np.random.choice(range(1, 3), num_interactions) # Generate random user-item interactions From 3dd2b7c9f2d116aa7308bb1677449e7c3a798a5d Mon Sep 17 00:00:00 2001 From: Ahmed Khalid Date: Wed, 18 Dec 2024 10:35:51 +0000 Subject: [PATCH 09/33] run on gpu 6 --- .devcontainer/docker-compose.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml index 0dccd188..46271d06 100644 --- a/.devcontainer/docker-compose.yml +++ b/.devcontainer/docker-compose.yml @@ -9,4 +9,5 @@ services: devices: - driver: nvidia capabilities: [gpu] - count: 1 # Assign number of GPUs or use 'all' to assign all available GPUs \ No newline at end of file + # count: 1 # Assign number of GPUs or use 'all' to assign all available GPUs + device_ids: ["6"] \ No newline at end of file From e9bdcd6a605e95756ad34caad8cabc811b563b35 Mon Sep 17 00:00:00 2001 From: Ahmed Khalid Date: Wed, 18 Dec 2024 10:44:45 +0000 Subject: [PATCH 10/33] add requirments --- requirments.txt | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 requirments.txt diff --git a/requirments.txt b/requirments.txt new file mode 100644 index 00000000..12b86c03 --- /dev/null +++ b/requirments.txt @@ -0,0 +1,7 @@ +numpy +numba +pycuda +scipy +pandas +kagglehub +scikit-learn \ No newline at end of file From da31b30562feb78fd0ed42869c144c3f0dc4a2d6 Mon Sep 17 00:00:00 2001 From: Ahmed Khalid Date: Wed, 18 Dec 2024 13:27:36 +0000 Subject: [PATCH 11/33] update --- .../applications/RecommendationSystems.py | 166 ++++++++++++++---- 1 file changed, 130 insertions(+), 36 deletions(-) diff --git a/examples/applications/RecommendationSystems.py b/examples/applications/RecommendationSystems.py index a453d42d..eea88e73 100644 --- a/examples/applications/RecommendationSystems.py +++ b/examples/applications/RecommendationSystems.py @@ -11,18 +11,17 @@ def default_args(**kwargs): parser = argparse.ArgumentParser() parser.add_argument("--epochs", default=250, type=int) - parser.add_argument("--number-of-clauses", default=1000, type=int) - parser.add_argument("--T", default=1000, type=int) + parser.add_argument("--number-of-clauses", default=10000, type=int) + parser.add_argument("--T", default=10000, type=int) parser.add_argument("--s", default=10.0, type=float) parser.add_argument("--number-of-state-bits", default=8, type=int) - parser.add_argument("--depth", default=3, type=int) - parser.add_argument("--hypervector-size", default=1024, type=int) - parser.add_argument("--hypervector-bits", default=10, type=int) - parser.add_argument("--message-size", default=512, type=int) - parser.add_argument("--message-bits", default=2, type=int) + parser.add_argument("--depth", default=1, type=int) + parser.add_argument("--hypervector-size", default=4096, type=int) + parser.add_argument("--hypervector-bits", default=256, type=int) + parser.add_argument("--message-size", default=4096, type=int) + parser.add_argument("--message-bits", default=256, type=int) parser.add_argument('--double-hashing', dest='double_hashing', default=False, action='store_true') parser.add_argument("--noise", default=0.01, type=float) - parser.add_argument("--number-of-examples", default=500, type=int) parser.add_argument("--max-included-literals", default=4, type=int) args = parser.parse_args() @@ -33,38 +32,133 @@ def default_args(**kwargs): args = default_args() -# print("Creating training data") -# path = kagglehub.dataset_download("karkavelrajaj/amazon-sales-dataset") -# print("Path to dataset files:", path) -# data_file = path + "/amazon.csv" -# data = pd.read_csv(data_file) -# # print("Data preview:", data.head()) -# data = data[['product_id', 'category', 'user_id', 'rating']] +############################# real dataset ######################## + +print("Creating training data") +path = kagglehub.dataset_download("karkavelrajaj/amazon-sales-dataset") +print("Path to dataset files:", path) +data_file = path + "/amazon.csv" +data = pd.read_csv(data_file) +# print("Data preview:", data.head()) +data = data[['product_id', 'category', 'user_id', 'rating']] ############################# artificial dataset ######################## + # Set random seed for reproducibility -np.random.seed(42) -# Define the size of the artificial dataset -num_users = 10 # Number of unique users -num_items = 50 # Number of unique items -num_categories = 10 # Number of unique categories -num_interactions = 100000 # Number of user-item interactions -# Generate random ratings (e.g., between 1 and 5) -ratings = np.random.choice(range(1, 3), num_interactions) -# Generate random user-item interactions -user_ids = np.random.choice(range(num_users), num_interactions) -item_ids = np.random.choice(range(num_items), num_interactions) -categories = np.random.choice(range(num_categories), num_interactions) -# Combine into a DataFrame -data = pd.DataFrame({ - 'user_id': user_ids, - 'product_id': item_ids, - 'category': categories, - 'rating': ratings -}) -print("Artificial Dataset Preview:") -print(data.head()) +# np.random.seed(42) + +########################## ver 1 ############################ + +# num_users = 5 # Number of unique users +# num_items =10 # Number of unique items +# num_categories = 5 # Number of unique categories +# num_interactions = 1000 # Number of user-item interactions +# # Generate random ratings (e.g., between 1 and 5) +# ratings = np.random.choice(range(1, 3), num_interactions) +# # Generate random user-item interactions +# user_ids = np.random.choice(range(num_users), num_interactions) +# item_ids = np.random.choice(range(num_items), num_interactions) +# categories = np.random.choice(range(num_categories), num_interactions) + +# data = pd.DataFrame({ +# 'user_id': user_ids, +# 'product_id': item_ids, +# 'category': categories, +# 'rating': ratings +# }) +# print("Artificial Dataset Preview:") + +########################## ver 2 ############################ + +# Parameters +# num_users = 100 # Number of unique users +# num_items = 50 # Number of unique items +# num_categories = 50 # Number of unique categories +# num_interactions = 1000 # Number of user-item interactions +# noise_ratio = 0.01 # Percentage of noisy interactions + +# # Generate user preferences: each user prefers 1-3 random categories +# user_preferences = { +# user: np.random.choice(range(num_categories), size=np.random.randint(1, 4), replace=False) +# for user in range(num_users) +# } + +# # Assign each item to a category +# item_categories = {item: np.random.choice(range(num_categories)) for item in range(num_items)} + +# # Generate interactions +# user_ids = np.random.choice(range(num_users), num_interactions) +# item_ids = np.random.choice(range(num_items), num_interactions) + +# # Generate ratings based on the pattern +# ratings = [] +# for user, item in zip(user_ids, item_ids): +# item_category = item_categories[item] +# if item_category in user_preferences[user]: +# ratings.append(np.random.choice([3, 4])) # High rating for preferred categories +# else: +# ratings.append(np.random.choice([1, 2])) # Low rating otherwise + +# # Introduce noise +# num_noisy = int(noise_ratio * num_interactions) +# noisy_indices = np.random.choice(range(num_interactions), num_noisy, replace=False) +# for idx in noisy_indices: +# ratings[idx] = np.random.choice(range(1, 6)) # Replace with random rating + +# # Combine into a DataFrame +# data = pd.DataFrame({ +# 'user_id': user_ids, +# 'product_id': item_ids, +# 'category': [item_categories[item] for item in item_ids], +# 'rating': ratings +# }) +# print("Artificial Dataset Preview:") + +########################### ver 3 ############################## + +# Parameters +# num_users = 100 # Number of unique users +# num_items = 50 # Number of unique items +# num_categories = 5 # Number of unique categories +# num_interactions = 10000 # Number of user-item interactions +# noise_ratio = 0.01 # Percentage of noisy interactions + +# # Step 1: Define deterministic user preferences +# user_preferences = {user: user % num_categories for user in range(num_users)} + +# # Step 2: Assign items to categories in a cyclic pattern +# item_categories = {item: item % num_categories for item in range(num_items)} + +# # Step 3: Generate deterministic interactions +# user_ids = np.arange(num_interactions) % num_users # Cycle through users +# item_ids = np.arange(num_interactions) % num_items # Cycle through items + +# # Step 4: Generate ratings based on the pattern +# ratings = [] +# for user, item in zip(user_ids, item_ids): +# preferred_category = user_preferences[user] +# item_category = item_categories[item] +# if item_category == preferred_category: +# ratings.append(5) # High rating for preferred category +# else: +# ratings.append(1) # Low rating otherwise + +# # Step 5: Introduce noise +# num_noisy = int(noise_ratio * num_interactions) +# noisy_indices = np.random.choice(range(num_interactions), num_noisy, replace=False) +# for idx in noisy_indices: +# ratings[idx] = np.random.choice(range(1, 6)) # Replace with random rating + +# # Step 6: Create a DataFrame +# data = pd.DataFrame({ +# 'user_id': user_ids, +# 'product_id': item_ids, +# 'category': [item_categories[item] for item in item_ids], +# 'rating': ratings +# }) + ######################################################################## +print(data.head()) le_user = LabelEncoder() le_item = LabelEncoder() From fababa59963ca02253cdb8ff9a1f9127d228c607 Mon Sep 17 00:00:00 2001 From: Ahmed Khalid Date: Wed, 18 Dec 2024 13:47:42 +0000 Subject: [PATCH 12/33] expanded ds --- .../applications/RecommendationSystems.py | 34 +++++++++++++++++-- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/examples/applications/RecommendationSystems.py b/examples/applications/RecommendationSystems.py index eea88e73..db7000fb 100644 --- a/examples/applications/RecommendationSystems.py +++ b/examples/applications/RecommendationSystems.py @@ -38,10 +38,38 @@ def default_args(**kwargs): path = kagglehub.dataset_download("karkavelrajaj/amazon-sales-dataset") print("Path to dataset files:", path) data_file = path + "/amazon.csv" -data = pd.read_csv(data_file) +org_data = pd.read_csv(data_file) # print("Data preview:", data.head()) -data = data[['product_id', 'category', 'user_id', 'rating']] - +org_data = org_data[['product_id', 'category', 'user_id', 'rating']] +#################################### expanded +org_data['rating'] = pd.to_numeric(org_data['rating'], errors='coerce') # Coerce invalid values to NaN +org_data.dropna(subset=['rating'], inplace=True) # Drop rows with NaN ratings +org_data['rating'] = org_data['rating'].astype(int) +# Expand the dataset 10 times +data = pd.concat([org_data] * 10, ignore_index=True) + +# Shuffle the expanded dataset +data = data.sample(frac=1, random_state=42).reset_index(drop=True) + +# Add noise +# Define the noise ratio +noise_ratio = 0.1 # 10% noise + +# Select rows to apply noise +num_noisy_rows = int(noise_ratio * len(data)) +noisy_indices = np.random.choice(data.index, size=num_noisy_rows, replace=False) + +# Add noise to ratings +data.loc[noisy_indices, 'rating'] = np.random.choice(range(1, 6), size=num_noisy_rows) + +# Add noise to categories +unique_categories = data['category'].unique() +data.loc[noisy_indices, 'category'] = np.random.choice(unique_categories, size=num_noisy_rows) + +# Print a preview of the noisy and expanded dataset +print("Original data shape:", org_data.shape) +print("Expanded data shape:", data.shape) +print("Data preview:\n", data.head()) ############################# artificial dataset ######################## # Set random seed for reproducibility From 82305ab67649e458064ee4bfb5d732da250b433b Mon Sep 17 00:00:00 2001 From: Ahmed Khalid Date: Thu, 19 Dec 2024 14:57:03 +0000 Subject: [PATCH 13/33] update --- .../applications/RecommendationSystems.py | 2 +- examples/applications/test.ipynb | 226 +++++++++++++++--- 2 files changed, 199 insertions(+), 29 deletions(-) diff --git a/examples/applications/RecommendationSystems.py b/examples/applications/RecommendationSystems.py index db7000fb..4a1daa46 100644 --- a/examples/applications/RecommendationSystems.py +++ b/examples/applications/RecommendationSystems.py @@ -22,7 +22,7 @@ def default_args(**kwargs): parser.add_argument("--message-bits", default=256, type=int) parser.add_argument('--double-hashing', dest='double_hashing', default=False, action='store_true') parser.add_argument("--noise", default=0.01, type=float) - parser.add_argument("--max-included-literals", default=4, type=int) + parser.add_argument("--max-included-literals", default=10, type=int) args = parser.parse_args() for key, value in kwargs.items(): diff --git a/examples/applications/test.ipynb b/examples/applications/test.ipynb index 44e02947..7d389f1b 100644 --- a/examples/applications/test.ipynb +++ b/examples/applications/test.ipynb @@ -2,66 +2,138 @@ "cells": [ { "cell_type": "code", - "execution_count": 19, + "execution_count": 1, "metadata": {}, "outputs": [ { - "name": "stdout", + "name": "stderr", "output_type": "stream", "text": [ - "Creating training data\n", - "Path to dataset files: /root/.cache/kagglehub/datasets/karkavelrajaj/amazon-sales-dataset/versions/1\n", - "Electronics|HomeTheater,TV&Video|Accessories|RemoteControls\n", - "X_train shape: (1172, 3)\n", - "y_train shape: (1172,)\n", - "X_test shape: (293, 3)\n", - "y_test shape: (293,)\n", - "111\n", - "Electronics|HomeTheater,TV&Video|Accessories|RemoteControls\n" + "/usr/local/lib/python3.10/dist-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", + " from .autonotebook import tqdm as notebook_tqdm\n", + "usage: ipykernel_launcher.py [-h] [--epochs EPOCHS]\n", + " [--number-of-clauses NUMBER_OF_CLAUSES] [--T T]\n", + " [--s S]\n", + " [--number-of-state-bits NUMBER_OF_STATE_BITS]\n", + " [--depth DEPTH]\n", + " [--hypervector-size HYPERVECTOR_SIZE]\n", + " [--hypervector-bits HYPERVECTOR_BITS]\n", + " [--message-size MESSAGE_SIZE]\n", + " [--message-bits MESSAGE_BITS] [--double-hashing]\n", + " [--noise NOISE]\n", + " [--max-included-literals MAX_INCLUDED_LITERALS]\n", + "ipykernel_launcher.py: error: unrecognized arguments: --f=/root/.local/share/jupyter/runtime/kernel-v3a1695e0e67c01cd0a818bc897e0f886c634ee3d4.json\n" + ] + }, + { + "ename": "SystemExit", + "evalue": "2", + "output_type": "error", + "traceback": [ + "An exception has occurred, use %tb to see the full traceback.\n", + "\u001b[0;31mSystemExit\u001b[0m\u001b[0;31m:\u001b[0m 2\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/root/.local/lib/python3.10/site-packages/IPython/core/interactiveshell.py:3585: UserWarning: To exit: use 'exit', 'quit', or Ctrl-D.\n", + " warn(\"To exit: use 'exit', 'quit', or Ctrl-D.\", stacklevel=1)\n" ] } ], "source": [ "from GraphTsetlinMachine.graphs import Graphs\n", - "import numpy as np\n", - "from scipy.sparse import csr_matrix\n", "from GraphTsetlinMachine.tm import MultiClassGraphTsetlinMachine\n", "from time import time\n", "import argparse\n", - "import random\n", "import pandas as pd\n", + "import numpy as np\n", "import kagglehub\n", "from sklearn.model_selection import train_test_split\n", "from sklearn.preprocessing import LabelEncoder\n", "\n", + "def default_args(**kwargs):\n", + " parser = argparse.ArgumentParser()\n", + " parser.add_argument(\"--epochs\", default=250, type=int)\n", + " parser.add_argument(\"--number-of-clauses\", default=10000, type=int)\n", + " parser.add_argument(\"--T\", default=10000, type=int)\n", + " parser.add_argument(\"--s\", default=10.0, type=float)\n", + " parser.add_argument(\"--number-of-state-bits\", default=8, type=int)\n", + " parser.add_argument(\"--depth\", default=1, type=int)\n", + " parser.add_argument(\"--hypervector-size\", default=4096, type=int)\n", + " parser.add_argument(\"--hypervector-bits\", default=256, type=int)\n", + " parser.add_argument(\"--message-size\", default=4096, type=int)\n", + " parser.add_argument(\"--message-bits\", default=256, type=int)\n", + " parser.add_argument('--double-hashing', dest='double_hashing', default=False, action='store_true')\n", + " parser.add_argument(\"--noise\", default=0.01, type=float)\n", + " parser.add_argument(\"--max-included-literals\", default=10, type=int)\n", + "\n", + " args = parser.parse_args()\n", + " for key, value in kwargs.items():\n", + " if key in args.__dict__:\n", + " setattr(args, key, value)\n", + " return args\n", + "\n", + "args = default_args()\n", + "\n", + "############################# real dataset ########################\n", "\n", "print(\"Creating training data\")\n", "path = kagglehub.dataset_download(\"karkavelrajaj/amazon-sales-dataset\")\n", "print(\"Path to dataset files:\", path)\n", - "data_file = path + \"/amazon.csv\" # Adjust this path if necessary\n", - "data = pd.read_csv(data_file)\n", + "data_file = path + \"/amazon.csv\" \n", + "org_data = pd.read_csv(data_file)\n", "# print(\"Data preview:\", data.head())\n", - "data = data[['product_id', 'category', 'user_id', 'rating']]\n", - "print(data['category'][100])\n", + "org_data = org_data[['product_id', 'category', 'user_id', 'rating']]\n", + "#################################### expanded \n", + "org_data['rating'] = pd.to_numeric(org_data['rating'], errors='coerce') # Coerce invalid values to NaN\n", + "org_data.dropna(subset=['rating'], inplace=True) # Drop rows with NaN ratings\n", + "org_data['rating'] = org_data['rating'].astype(int)\n", + "# Expand the dataset 10 times\n", + "data = pd.concat([org_data] * 10, ignore_index=True)\n", + "\n", + "# Shuffle the expanded dataset\n", + "data = data.sample(frac=1, random_state=42).reset_index(drop=True)\n", + "\n", + "# Add noise\n", + "# Define the noise ratio\n", + "noise_ratio = 0.1 # 10% noise\n", + "\n", + "# Select rows to apply noise\n", + "num_noisy_rows = int(noise_ratio * len(data))\n", + "noisy_indices = np.random.choice(data.index, size=num_noisy_rows, replace=False)\n", + "\n", + "# Add noise to ratings\n", + "data.loc[noisy_indices, 'rating'] = np.random.choice(range(1, 6), size=num_noisy_rows)\n", + "\n", + "# Add noise to categories\n", + "unique_categories = data['category'].unique()\n", + "data.loc[noisy_indices, 'category'] = np.random.choice(unique_categories, size=num_noisy_rows)\n", + "\n", + "# Print a preview of the noisy and expanded dataset\n", + "print(\"Original data shape:\", org_data.shape)\n", + "print(\"Expanded data shape:\", data.shape)\n", + "print(\"Data preview:\\n\", data.head())\n", + "\n", + "print(data.head())\n", " \n", - "# Step 2: Encode user_id, product_id, and category with LabelEncoder\n", - "# This converts string identifiers into unique integer values\n", "le_user = LabelEncoder()\n", "le_item = LabelEncoder()\n", "le_category = LabelEncoder()\n", + "le_rating = LabelEncoder() \n", "\n", "data['user_id'] = le_user.fit_transform(data['user_id'])\n", "data['product_id'] = le_item.fit_transform(data['product_id'])\n", "data['category'] = le_category.fit_transform(data['category'])\n", + "data['rating'] = le_rating.fit_transform(data['rating'])\n", "\n", - "# Step 3: Prepare X (features) and y (labels)\n", - "x = data[['user_id', 'product_id', 'category']].values # Features: [user, item, category]\n", - "y = data['rating'].values # Labels: rating\n", + "x = data[['user_id', 'product_id', 'category']].values \n", + "y = data['rating'].values \n", "\n", - "# Step 4: Split the data into training and test sets\n", "X_train, X_test, Y_train, Y_test = train_test_split(x, y, test_size=0.2, random_state=42)\n", "\n", - "# Display the shapes to verify the split\n", "print(\"X_train shape:\", X_train.shape)\n", "print(\"y_train shape:\", Y_train.shape)\n", "print(\"X_test shape:\", X_test.shape)\n", @@ -71,9 +143,107 @@ "items = data['product_id'].unique()\n", "categories = data['category'].unique()\n", "\n", - "print(categories[100])\n", - "original_user_id = le_category.inverse_transform([data['category'][100]])[0]\n", - "print(original_user_id)" + "# Initialize Graphs with symbols for GTM\n", + "number_of_nodes = 3\n", + "symbols = []\n", + "symbols = [\"U_\" + str(u) for u in users] + [\"I_\" + str(i) for i in items] + [\"C_\" + str(c) for c in categories] \n", + "print(len(symbols))\n", + "# Train data\n", + "graphs_train = Graphs(\n", + " X_train.shape[0],\n", + " symbols=symbols,\n", + " hypervector_size=args.hypervector_size,\n", + " hypervector_bits=args.hypervector_bits,\n", + " double_hashing = args.double_hashing\n", + ")\n", + "for graph_id in range(X_train.shape[0]):\n", + " graphs_train.set_number_of_graph_nodes(graph_id, number_of_nodes)\n", + "graphs_train.prepare_node_configuration()\n", + "for graph_id in range(X_train.shape[0]):\n", + " for node_id in range(graphs_train.number_of_graph_nodes[graph_id]):\n", + " number_of_edges = 2 if node_id > 0 and node_id < graphs_train.number_of_graph_nodes[graph_id]-1 else 1\n", + " if node_id == 0:\n", + " graphs_train.add_graph_node(graph_id, \"User\", number_of_edges)\n", + " elif node_id == 1:\n", + " graphs_train.add_graph_node(graph_id, \"Item\", number_of_edges)\n", + " else:\n", + " graphs_train.add_graph_node(graph_id, \"Category\", number_of_edges)\n", + "graphs_train.prepare_edge_configuration()\n", + "for graph_id in range(X_train.shape[0]):\n", + " for node_id in range(graphs_train.number_of_graph_nodes[graph_id]):\n", + " if node_id == 0:\n", + " graphs_train.add_graph_node_edge(graph_id, \"User\", \"Item\", \"UserItem\")\n", + " \n", + " if node_id == 1:\n", + " graphs_train.add_graph_node_edge(graph_id, \"Item\", \"Category\", \"ItemCategory\")\n", + " graphs_train.add_graph_node_edge(graph_id, \"Item\", \"User\", \"ItemUser\")\n", + " \n", + " if node_id == 2:\n", + " graphs_train.add_graph_node_edge(graph_id, \"Category\", \"Item\", \"CatrgoryItem\")\n", + "\n", + " graphs_train.add_graph_node_property(graph_id, \"User\", \"U_\" + str(X_train[graph_id][0]))\n", + " graphs_train.add_graph_node_property(graph_id, \"Item\", \"I_\" + str(X_train[graph_id][1]))\n", + " graphs_train.add_graph_node_property(graph_id, \"Category\", \"C_\" + str(X_train[graph_id][2]))\n", + "graphs_train.encode()\n", + "print(\"Training data produced\")\n", + "\n", + "# Test data\n", + "graphs_test = Graphs(X_test.shape[0], init_with=graphs_train)\n", + "for graph_id in range(X_test.shape[0]):\n", + " graphs_test.set_number_of_graph_nodes(graph_id, number_of_nodes)\n", + "graphs_test.prepare_node_configuration()\n", + "for graph_id in range(X_test.shape[0]):\n", + " for node_id in range(graphs_test.number_of_graph_nodes[graph_id]):\n", + " number_of_edges = 2 if node_id > 0 and node_id < graphs_test.number_of_graph_nodes[graph_id]-1 else 1\n", + " if node_id == 0:\n", + " graphs_test.add_graph_node(graph_id, \"User\", number_of_edges)\n", + " elif node_id == 1:\n", + " graphs_test.add_graph_node(graph_id, \"Item\", number_of_edges)\n", + " else:\n", + " graphs_test.add_graph_node(graph_id, \"Category\", number_of_edges)\n", + "graphs_test.prepare_edge_configuration()\n", + "for graph_id in range(X_test.shape[0]):\n", + " for node_id in range(graphs_test.number_of_graph_nodes[graph_id]):\n", + " if node_id == 0:\n", + " graphs_test.add_graph_node_edge(graph_id, \"User\", \"Item\", \"UserItem\")\n", + " \n", + " if node_id == 1:\n", + " graphs_test.add_graph_node_edge(graph_id, \"Item\", \"Category\", \"ItemCategory\")\n", + " graphs_test.add_graph_node_edge(graph_id, \"Item\", \"User\", \"ItemUser\")\n", + " \n", + " if node_id == 2:\n", + " graphs_test.add_graph_node_edge(graph_id, \"Category\", \"Item\", \"CatrgoryItem\")\n", + "\n", + " graphs_test.add_graph_node_property(graph_id, \"User\", \"U_\" + str(X_test[graph_id][0]))\n", + " graphs_test.add_graph_node_property(graph_id, \"Item\", \"I_\" + str(X_test[graph_id][1]))\n", + " graphs_test.add_graph_node_property(graph_id, \"Category\", \"C_\" + str(X_test[graph_id][2]))\n", + "graphs_test.encode()\n", + "print(\"Testing data produced\")\n", + "\n", + "tm = MultiClassGraphTsetlinMachine(\n", + " args.number_of_clauses,\n", + " args.T,\n", + " args.s,\n", + " number_of_state_bits = args.number_of_state_bits,\n", + " depth=args.depth,\n", + " message_size=args.message_size,\n", + " message_bits=args.message_bits,\n", + " max_included_literals=args.max_included_literals,\n", + " double_hashing = args.double_hashing\n", + ")\n", + "\n", + "for i in range(args.epochs):\n", + " start_training = time()\n", + " tm.fit(graphs_train, Y_train, epochs=1, incremental=True)\n", + " stop_training = time()\n", + "\n", + " start_testing = time()\n", + " result_test = 100*(tm.predict(graphs_test) == Y_test).mean()\n", + " stop_testing = time()\n", + "\n", + " result_train = 100*(tm.predict(graphs_train) == Y_train).mean()\n", + "\n", + " print(\"%d %.2f %.2f %.2f %.2f\" % (i, result_train, result_test, stop_training-start_training, stop_testing-start_testing))" ] } ], From 218a96f11d24076ac1076c4d153b280b778700a0 Mon Sep 17 00:00:00 2001 From: Ahmed Khalid Date: Fri, 20 Dec 2024 10:16:33 +0000 Subject: [PATCH 14/33] before add example no --- .../applications/RecommendationSystems.py | 47 ++++++++++--------- examples/applications/test.ipynb | 2 +- 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/examples/applications/RecommendationSystems.py b/examples/applications/RecommendationSystems.py index 4a1daa46..2b57ecc4 100644 --- a/examples/applications/RecommendationSystems.py +++ b/examples/applications/RecommendationSystems.py @@ -10,19 +10,20 @@ def default_args(**kwargs): parser = argparse.ArgumentParser() - parser.add_argument("--epochs", default=250, type=int) + parser.add_argument("--epochs", default=10, type=int) parser.add_argument("--number-of-clauses", default=10000, type=int) parser.add_argument("--T", default=10000, type=int) parser.add_argument("--s", default=10.0, type=float) parser.add_argument("--number-of-state-bits", default=8, type=int) - parser.add_argument("--depth", default=1, type=int) + parser.add_argument("--depth", default=3, type=int) parser.add_argument("--hypervector-size", default=4096, type=int) parser.add_argument("--hypervector-bits", default=256, type=int) - parser.add_argument("--message-size", default=4096, type=int) - parser.add_argument("--message-bits", default=256, type=int) + parser.add_argument("--message-size", default=256, type=int) + parser.add_argument("--message-bits", default=2, type=int) parser.add_argument('--double-hashing', dest='double_hashing', default=False, action='store_true') parser.add_argument("--noise", default=0.01, type=float) parser.add_argument("--max-included-literals", default=10, type=int) + parser.add_argument("--number-of-examples", default=1000, type=int) args = parser.parse_args() for key, value in kwargs.items(): @@ -314,25 +315,25 @@ def default_args(**kwargs): print("%d %.2f %.2f %.2f %.2f" % (i, result_train, result_test, stop_training-start_training, stop_testing-start_testing)) -# weights = tm.get_state()[1].reshape(2, -1) -# for i in range(tm.number_of_clauses): -# print("Clause #%d W:(%d %d)" % (i, weights[0,i], weights[1,i]), end=' ') -# l = [] -# for k in range(args.hypervector_size * 2): -# if tm.ta_action(0, i, k): -# if k < args.hypervector_size: -# l.append("x%d" % (k)) -# else: -# l.append("NOT x%d" % (k - args.hypervector_size)) - -# for k in range(args.message_size * 2): -# if tm.ta_action(1, i, k): -# if k < args.message_size: -# l.append("c%d" % (k)) -# else: -# l.append("NOT c%d" % (k - args.message_size)) - -# print(" AND ".join(l)) +weights = tm.get_state()[1].reshape(2, -1) +for i in range(tm.number_of_clauses): + print("Clause #%d W:(%d %d)" % (i, weights[0,i], weights[1,i]), end=' ') + l = [] + for k in range(args.hypervector_size * 2): + if tm.ta_action(0, i, k): + if k < args.hypervector_size: + l.append("x%d" % (k)) + else: + l.append("NOT x%d" % (k - args.hypervector_size)) + + for k in range(args.message_size * 2): + if tm.ta_action(1, i, k): + if k < args.message_size: + l.append("c%d" % (k)) + else: + l.append("NOT c%d" % (k - args.message_size)) + + print(" AND ".join(l)) # print(graphs_test.hypervectors) # print(tm.hypervectors) diff --git a/examples/applications/test.ipynb b/examples/applications/test.ipynb index 7d389f1b..1465bf14 100644 --- a/examples/applications/test.ipynb +++ b/examples/applications/test.ipynb @@ -22,7 +22,7 @@ " [--message-bits MESSAGE_BITS] [--double-hashing]\n", " [--noise NOISE]\n", " [--max-included-literals MAX_INCLUDED_LITERALS]\n", - "ipykernel_launcher.py: error: unrecognized arguments: --f=/root/.local/share/jupyter/runtime/kernel-v3a1695e0e67c01cd0a818bc897e0f886c634ee3d4.json\n" + "ipykernel_launcher.py: error: unrecognized arguments: --f=/root/.local/share/jupyter/runtime/kernel-v306f6e67794e909fd94dbef768cafee2e613728cc.json\n" ] }, { From 799493fd1d3241fb0a6e5271bbaeaecb8c9271cb Mon Sep 17 00:00:00 2001 From: Ahmed Khalid Date: Fri, 20 Dec 2024 11:49:12 +0000 Subject: [PATCH 15/33] orgnizing files --- .../applications/RecommendationSystems.py | 340 ------------------ .../prepare_dataset.cpython-310.pyc | Bin 0 -> 1415 bytes .../recommendation/main_products.py | 178 +++++++++ .../recommendation/prepare_dataset.py | 145 ++++++++ 4 files changed, 323 insertions(+), 340 deletions(-) delete mode 100644 examples/applications/RecommendationSystems.py create mode 100644 examples/applications/recommendation/__pycache__/prepare_dataset.cpython-310.pyc create mode 100644 examples/applications/recommendation/main_products.py create mode 100644 examples/applications/recommendation/prepare_dataset.py diff --git a/examples/applications/RecommendationSystems.py b/examples/applications/RecommendationSystems.py deleted file mode 100644 index 2b57ecc4..00000000 --- a/examples/applications/RecommendationSystems.py +++ /dev/null @@ -1,340 +0,0 @@ -from GraphTsetlinMachine.graphs import Graphs -from GraphTsetlinMachine.tm import MultiClassGraphTsetlinMachine -from time import time -import argparse -import pandas as pd -import numpy as np -import kagglehub -from sklearn.model_selection import train_test_split -from sklearn.preprocessing import LabelEncoder - -def default_args(**kwargs): - parser = argparse.ArgumentParser() - parser.add_argument("--epochs", default=10, type=int) - parser.add_argument("--number-of-clauses", default=10000, type=int) - parser.add_argument("--T", default=10000, type=int) - parser.add_argument("--s", default=10.0, type=float) - parser.add_argument("--number-of-state-bits", default=8, type=int) - parser.add_argument("--depth", default=3, type=int) - parser.add_argument("--hypervector-size", default=4096, type=int) - parser.add_argument("--hypervector-bits", default=256, type=int) - parser.add_argument("--message-size", default=256, type=int) - parser.add_argument("--message-bits", default=2, type=int) - parser.add_argument('--double-hashing', dest='double_hashing', default=False, action='store_true') - parser.add_argument("--noise", default=0.01, type=float) - parser.add_argument("--max-included-literals", default=10, type=int) - parser.add_argument("--number-of-examples", default=1000, type=int) - - args = parser.parse_args() - for key, value in kwargs.items(): - if key in args.__dict__: - setattr(args, key, value) - return args - -args = default_args() - -############################# real dataset ######################## - -print("Creating training data") -path = kagglehub.dataset_download("karkavelrajaj/amazon-sales-dataset") -print("Path to dataset files:", path) -data_file = path + "/amazon.csv" -org_data = pd.read_csv(data_file) -# print("Data preview:", data.head()) -org_data = org_data[['product_id', 'category', 'user_id', 'rating']] -#################################### expanded -org_data['rating'] = pd.to_numeric(org_data['rating'], errors='coerce') # Coerce invalid values to NaN -org_data.dropna(subset=['rating'], inplace=True) # Drop rows with NaN ratings -org_data['rating'] = org_data['rating'].astype(int) -# Expand the dataset 10 times -data = pd.concat([org_data] * 10, ignore_index=True) - -# Shuffle the expanded dataset -data = data.sample(frac=1, random_state=42).reset_index(drop=True) - -# Add noise -# Define the noise ratio -noise_ratio = 0.1 # 10% noise - -# Select rows to apply noise -num_noisy_rows = int(noise_ratio * len(data)) -noisy_indices = np.random.choice(data.index, size=num_noisy_rows, replace=False) - -# Add noise to ratings -data.loc[noisy_indices, 'rating'] = np.random.choice(range(1, 6), size=num_noisy_rows) - -# Add noise to categories -unique_categories = data['category'].unique() -data.loc[noisy_indices, 'category'] = np.random.choice(unique_categories, size=num_noisy_rows) - -# Print a preview of the noisy and expanded dataset -print("Original data shape:", org_data.shape) -print("Expanded data shape:", data.shape) -print("Data preview:\n", data.head()) -############################# artificial dataset ######################## - -# Set random seed for reproducibility -# np.random.seed(42) - -########################## ver 1 ############################ - -# num_users = 5 # Number of unique users -# num_items =10 # Number of unique items -# num_categories = 5 # Number of unique categories -# num_interactions = 1000 # Number of user-item interactions -# # Generate random ratings (e.g., between 1 and 5) -# ratings = np.random.choice(range(1, 3), num_interactions) -# # Generate random user-item interactions -# user_ids = np.random.choice(range(num_users), num_interactions) -# item_ids = np.random.choice(range(num_items), num_interactions) -# categories = np.random.choice(range(num_categories), num_interactions) - -# data = pd.DataFrame({ -# 'user_id': user_ids, -# 'product_id': item_ids, -# 'category': categories, -# 'rating': ratings -# }) -# print("Artificial Dataset Preview:") - -########################## ver 2 ############################ - -# Parameters -# num_users = 100 # Number of unique users -# num_items = 50 # Number of unique items -# num_categories = 50 # Number of unique categories -# num_interactions = 1000 # Number of user-item interactions -# noise_ratio = 0.01 # Percentage of noisy interactions - -# # Generate user preferences: each user prefers 1-3 random categories -# user_preferences = { -# user: np.random.choice(range(num_categories), size=np.random.randint(1, 4), replace=False) -# for user in range(num_users) -# } - -# # Assign each item to a category -# item_categories = {item: np.random.choice(range(num_categories)) for item in range(num_items)} - -# # Generate interactions -# user_ids = np.random.choice(range(num_users), num_interactions) -# item_ids = np.random.choice(range(num_items), num_interactions) - -# # Generate ratings based on the pattern -# ratings = [] -# for user, item in zip(user_ids, item_ids): -# item_category = item_categories[item] -# if item_category in user_preferences[user]: -# ratings.append(np.random.choice([3, 4])) # High rating for preferred categories -# else: -# ratings.append(np.random.choice([1, 2])) # Low rating otherwise - -# # Introduce noise -# num_noisy = int(noise_ratio * num_interactions) -# noisy_indices = np.random.choice(range(num_interactions), num_noisy, replace=False) -# for idx in noisy_indices: -# ratings[idx] = np.random.choice(range(1, 6)) # Replace with random rating - -# # Combine into a DataFrame -# data = pd.DataFrame({ -# 'user_id': user_ids, -# 'product_id': item_ids, -# 'category': [item_categories[item] for item in item_ids], -# 'rating': ratings -# }) -# print("Artificial Dataset Preview:") - -########################### ver 3 ############################## - -# Parameters -# num_users = 100 # Number of unique users -# num_items = 50 # Number of unique items -# num_categories = 5 # Number of unique categories -# num_interactions = 10000 # Number of user-item interactions -# noise_ratio = 0.01 # Percentage of noisy interactions - -# # Step 1: Define deterministic user preferences -# user_preferences = {user: user % num_categories for user in range(num_users)} - -# # Step 2: Assign items to categories in a cyclic pattern -# item_categories = {item: item % num_categories for item in range(num_items)} - -# # Step 3: Generate deterministic interactions -# user_ids = np.arange(num_interactions) % num_users # Cycle through users -# item_ids = np.arange(num_interactions) % num_items # Cycle through items - -# # Step 4: Generate ratings based on the pattern -# ratings = [] -# for user, item in zip(user_ids, item_ids): -# preferred_category = user_preferences[user] -# item_category = item_categories[item] -# if item_category == preferred_category: -# ratings.append(5) # High rating for preferred category -# else: -# ratings.append(1) # Low rating otherwise - -# # Step 5: Introduce noise -# num_noisy = int(noise_ratio * num_interactions) -# noisy_indices = np.random.choice(range(num_interactions), num_noisy, replace=False) -# for idx in noisy_indices: -# ratings[idx] = np.random.choice(range(1, 6)) # Replace with random rating - -# # Step 6: Create a DataFrame -# data = pd.DataFrame({ -# 'user_id': user_ids, -# 'product_id': item_ids, -# 'category': [item_categories[item] for item in item_ids], -# 'rating': ratings -# }) - -######################################################################## -print(data.head()) - -le_user = LabelEncoder() -le_item = LabelEncoder() -le_category = LabelEncoder() -le_rating = LabelEncoder() - -data['user_id'] = le_user.fit_transform(data['user_id']) -data['product_id'] = le_item.fit_transform(data['product_id']) -data['category'] = le_category.fit_transform(data['category']) -data['rating'] = le_rating.fit_transform(data['rating']) - -x = data[['user_id', 'product_id', 'category']].values -y = data['rating'].values - -X_train, X_test, Y_train, Y_test = train_test_split(x, y, test_size=0.2, random_state=42) - -print("X_train shape:", X_train.shape) -print("y_train shape:", Y_train.shape) -print("X_test shape:", X_test.shape) -print("y_test shape:", Y_test.shape) - -users = data['user_id'].unique() -items = data['product_id'].unique() -categories = data['category'].unique() - -# Initialize Graphs with symbols for GTM -number_of_nodes = 3 -symbols = [] -symbols = ["U_" + str(u) for u in users] + ["I_" + str(i) for i in items] + ["C_" + str(c) for c in categories] -print(len(symbols)) -# Train data -graphs_train = Graphs( - X_train.shape[0], - symbols=symbols, - hypervector_size=args.hypervector_size, - hypervector_bits=args.hypervector_bits, - double_hashing = args.double_hashing -) -for graph_id in range(X_train.shape[0]): - graphs_train.set_number_of_graph_nodes(graph_id, number_of_nodes) -graphs_train.prepare_node_configuration() -for graph_id in range(X_train.shape[0]): - for node_id in range(graphs_train.number_of_graph_nodes[graph_id]): - number_of_edges = 2 if node_id > 0 and node_id < graphs_train.number_of_graph_nodes[graph_id]-1 else 1 - if node_id == 0: - graphs_train.add_graph_node(graph_id, "User", number_of_edges) - elif node_id == 1: - graphs_train.add_graph_node(graph_id, "Item", number_of_edges) - else: - graphs_train.add_graph_node(graph_id, "Category", number_of_edges) -graphs_train.prepare_edge_configuration() -for graph_id in range(X_train.shape[0]): - for node_id in range(graphs_train.number_of_graph_nodes[graph_id]): - if node_id == 0: - graphs_train.add_graph_node_edge(graph_id, "User", "Item", "UserItem") - - if node_id == 1: - graphs_train.add_graph_node_edge(graph_id, "Item", "Category", "ItemCategory") - graphs_train.add_graph_node_edge(graph_id, "Item", "User", "ItemUser") - - if node_id == 2: - graphs_train.add_graph_node_edge(graph_id, "Category", "Item", "CatrgoryItem") - - graphs_train.add_graph_node_property(graph_id, "User", "U_" + str(X_train[graph_id][0])) - graphs_train.add_graph_node_property(graph_id, "Item", "I_" + str(X_train[graph_id][1])) - graphs_train.add_graph_node_property(graph_id, "Category", "C_" + str(X_train[graph_id][2])) -graphs_train.encode() -print("Training data produced") - -# Test data -graphs_test = Graphs(X_test.shape[0], init_with=graphs_train) -for graph_id in range(X_test.shape[0]): - graphs_test.set_number_of_graph_nodes(graph_id, number_of_nodes) -graphs_test.prepare_node_configuration() -for graph_id in range(X_test.shape[0]): - for node_id in range(graphs_test.number_of_graph_nodes[graph_id]): - number_of_edges = 2 if node_id > 0 and node_id < graphs_test.number_of_graph_nodes[graph_id]-1 else 1 - if node_id == 0: - graphs_test.add_graph_node(graph_id, "User", number_of_edges) - elif node_id == 1: - graphs_test.add_graph_node(graph_id, "Item", number_of_edges) - else: - graphs_test.add_graph_node(graph_id, "Category", number_of_edges) -graphs_test.prepare_edge_configuration() -for graph_id in range(X_test.shape[0]): - for node_id in range(graphs_test.number_of_graph_nodes[graph_id]): - if node_id == 0: - graphs_test.add_graph_node_edge(graph_id, "User", "Item", "UserItem") - - if node_id == 1: - graphs_test.add_graph_node_edge(graph_id, "Item", "Category", "ItemCategory") - graphs_test.add_graph_node_edge(graph_id, "Item", "User", "ItemUser") - - if node_id == 2: - graphs_test.add_graph_node_edge(graph_id, "Category", "Item", "CatrgoryItem") - - graphs_test.add_graph_node_property(graph_id, "User", "U_" + str(X_test[graph_id][0])) - graphs_test.add_graph_node_property(graph_id, "Item", "I_" + str(X_test[graph_id][1])) - graphs_test.add_graph_node_property(graph_id, "Category", "C_" + str(X_test[graph_id][2])) -graphs_test.encode() -print("Testing data produced") - -tm = MultiClassGraphTsetlinMachine( - args.number_of_clauses, - args.T, - args.s, - number_of_state_bits = args.number_of_state_bits, - depth=args.depth, - message_size=args.message_size, - message_bits=args.message_bits, - max_included_literals=args.max_included_literals, - double_hashing = args.double_hashing -) - -for i in range(args.epochs): - start_training = time() - tm.fit(graphs_train, Y_train, epochs=1, incremental=True) - stop_training = time() - - start_testing = time() - result_test = 100*(tm.predict(graphs_test) == Y_test).mean() - stop_testing = time() - - result_train = 100*(tm.predict(graphs_train) == Y_train).mean() - - print("%d %.2f %.2f %.2f %.2f" % (i, result_train, result_test, stop_training-start_training, stop_testing-start_testing)) - -weights = tm.get_state()[1].reshape(2, -1) -for i in range(tm.number_of_clauses): - print("Clause #%d W:(%d %d)" % (i, weights[0,i], weights[1,i]), end=' ') - l = [] - for k in range(args.hypervector_size * 2): - if tm.ta_action(0, i, k): - if k < args.hypervector_size: - l.append("x%d" % (k)) - else: - l.append("NOT x%d" % (k - args.hypervector_size)) - - for k in range(args.message_size * 2): - if tm.ta_action(1, i, k): - if k < args.message_size: - l.append("c%d" % (k)) - else: - l.append("NOT c%d" % (k - args.message_size)) - - print(" AND ".join(l)) - -# print(graphs_test.hypervectors) -# print(tm.hypervectors) -# print(graphs_test.edge_type_id) \ No newline at end of file diff --git a/examples/applications/recommendation/__pycache__/prepare_dataset.cpython-310.pyc b/examples/applications/recommendation/__pycache__/prepare_dataset.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..334918248b0038345364fc10337498871cad89c2 GIT binary patch literal 1415 zcmZ8hOK&SR7_~i*q)D2lZBzsjC}Kqr_jbjmssQQ2T^1yekh&T9j(w+-PR4`n^bsYi zvds^GRm+ZFfcPukvg)1{E6z75QZKf~kI%>7^LWheZjYh;@uMy-Q^x+H&GRO(`3j%= z41;2dSL~d(vp5&+9G%BXsOV>Qo~T$Qm{Y~SXVdf%H`rND<9{u}g1%Sy+-nRT8t*t~ zp5F^49{8Fh(Td#(&Q?st@3A%a(S5w)I1@NaR$`X4r=nmg{f)oRV5@VsJbGod^krR4 zyp?553njf=etaqIrM%LWl`}azmUFo@^$RB}?OqVTX}>)BPWp?9H|-4wCT~j|pDg=N z@Rzx}T8_W9Wl`3$YEct+Asc-%jYF@oMlEuml`3>|>2+c3ZRjkVwj@ez>(w+3Nn^{} zhn-7V6qUYMya}Tx2Qp=@>&i$KL<1!VL}f4;lGeyH3ULDm!w$hS@-1|YEi&5v&g>rY z_|NIFY?@rM|IVX>TSfV3u%AD*IW%i2av(4<1 zA|BRNr`sR^C`1mQEh!DTEG7keQYh~t{^WYTlv9hKqe*J~opI^QvoGX_)bWqM#FV$ko z?T45wJ+kHao0|qYw0h3iGdfsI9Vy*Pe!$1#NW{@djQKb3mq@kWxOSYm;R= qq%?t7&8__e>unwh`OGpIu&w#-=^X!wN`FD6c0__Mo)@|p^!^25{fROF literal 0 HcmV?d00001 diff --git a/examples/applications/recommendation/main_products.py b/examples/applications/recommendation/main_products.py new file mode 100644 index 00000000..03d5b641 --- /dev/null +++ b/examples/applications/recommendation/main_products.py @@ -0,0 +1,178 @@ +from GraphTsetlinMachine.graphs import Graphs +from GraphTsetlinMachine.tm import MultiClassGraphTsetlinMachine +from time import time +import argparse +import pandas as pd +import numpy as np +from sklearn.model_selection import train_test_split +from sklearn.preprocessing import LabelEncoder +import prepare_dataset + +def default_args(**kwargs): + parser = argparse.ArgumentParser() + parser.add_argument("--epochs", default=10, type=int) + parser.add_argument("--number-of-clauses", default=1000, type=int) + parser.add_argument("--T", default=10000, type=int) + parser.add_argument("--s", default=10.0, type=float) + parser.add_argument("--number-of-state-bits", default=8, type=int) + parser.add_argument("--depth", default=3, type=int) + parser.add_argument("--hypervector-size", default=4096, type=int) + parser.add_argument("--hypervector-bits", default=256, type=int) + parser.add_argument("--message-size", default=256, type=int) + parser.add_argument("--message-bits", default=2, type=int) + parser.add_argument('--double-hashing', dest='double_hashing', default=False, action='store_true') + parser.add_argument("--noise", default=0.01, type=float) + parser.add_argument("--max-included-literals", default=10, type=int) + + args = parser.parse_args() + for key, value in kwargs.items(): + if key in args.__dict__: + setattr(args, key, value) + return args +args = default_args() + +data = prepare_dataset.aug_amazon_products() +print(data.head()) +le_user = LabelEncoder() +le_item = LabelEncoder() +le_category = LabelEncoder() +le_rating = LabelEncoder() +data['user_id'] = le_user.fit_transform(data['user_id']) +data['product_id'] = le_item.fit_transform(data['product_id']) +data['category'] = le_category.fit_transform(data['category']) +data['rating'] = le_rating.fit_transform(data['rating']) +x = data[['user_id', 'product_id', 'category']].values +y = data['rating'].values +X_train, X_test, Y_train, Y_test = train_test_split(x, y, test_size=0.2, random_state=42) +print("X_train shape:", X_train.shape) +print("y_train shape:", Y_train.shape) +print("X_test shape:", X_test.shape) +print("y_test shape:", Y_test.shape) +users = data['user_id'].unique() +items = data['product_id'].unique() +categories = data['category'].unique() +# Initialize Graphs with symbols for GTM +number_of_nodes = 3 +symbols = [] +symbols = ["U_" + str(u) for u in users] + ["I_" + str(i) for i in items] + ["C_" + str(c) for c in categories] +print("Symbols: ",len(symbols)) + +# Train data +graphs_train = Graphs( + X_train.shape[0], + symbols=symbols, + hypervector_size=args.hypervector_size, + hypervector_bits=args.hypervector_bits, + double_hashing = args.double_hashing +) +for graph_id in range(X_train.shape[0]): + graphs_train.set_number_of_graph_nodes(graph_id, number_of_nodes) +graphs_train.prepare_node_configuration() +for graph_id in range(X_train.shape[0]): + for node_id in range(graphs_train.number_of_graph_nodes[graph_id]): + number_of_edges = 2 if node_id > 0 and node_id < graphs_train.number_of_graph_nodes[graph_id]-1 else 1 + if node_id == 0: + graphs_train.add_graph_node(graph_id, "User", number_of_edges) + elif node_id == 1: + graphs_train.add_graph_node(graph_id, "Item", number_of_edges) + else: + graphs_train.add_graph_node(graph_id, "Category", number_of_edges) +graphs_train.prepare_edge_configuration() +for graph_id in range(X_train.shape[0]): + for node_id in range(graphs_train.number_of_graph_nodes[graph_id]): + if node_id == 0: + graphs_train.add_graph_node_edge(graph_id, "User", "Item", "UserItem") + + if node_id == 1: + graphs_train.add_graph_node_edge(graph_id, "Item", "Category", "ItemCategory") + graphs_train.add_graph_node_edge(graph_id, "Item", "User", "ItemUser") + + if node_id == 2: + graphs_train.add_graph_node_edge(graph_id, "Category", "Item", "CatrgoryItem") + + graphs_train.add_graph_node_property(graph_id, "User", "U_" + str(X_train[graph_id][0])) + graphs_train.add_graph_node_property(graph_id, "Item", "I_" + str(X_train[graph_id][1])) + graphs_train.add_graph_node_property(graph_id, "Category", "C_" + str(X_train[graph_id][2])) +graphs_train.encode() +print("Training data produced") + +# Test data +graphs_test = Graphs(X_test.shape[0], init_with=graphs_train) +for graph_id in range(X_test.shape[0]): + graphs_test.set_number_of_graph_nodes(graph_id, number_of_nodes) +graphs_test.prepare_node_configuration() +for graph_id in range(X_test.shape[0]): + for node_id in range(graphs_test.number_of_graph_nodes[graph_id]): + number_of_edges = 2 if node_id > 0 and node_id < graphs_test.number_of_graph_nodes[graph_id]-1 else 1 + if node_id == 0: + graphs_test.add_graph_node(graph_id, "User", number_of_edges) + elif node_id == 1: + graphs_test.add_graph_node(graph_id, "Item", number_of_edges) + else: + graphs_test.add_graph_node(graph_id, "Category", number_of_edges) +graphs_test.prepare_edge_configuration() +for graph_id in range(X_test.shape[0]): + for node_id in range(graphs_test.number_of_graph_nodes[graph_id]): + if node_id == 0: + graphs_test.add_graph_node_edge(graph_id, "User", "Item", "UserItem") + + if node_id == 1: + graphs_test.add_graph_node_edge(graph_id, "Item", "Category", "ItemCategory") + graphs_test.add_graph_node_edge(graph_id, "Item", "User", "ItemUser") + + if node_id == 2: + graphs_test.add_graph_node_edge(graph_id, "Category", "Item", "CatrgoryItem") + + graphs_test.add_graph_node_property(graph_id, "User", "U_" + str(X_test[graph_id][0])) + graphs_test.add_graph_node_property(graph_id, "Item", "I_" + str(X_test[graph_id][1])) + graphs_test.add_graph_node_property(graph_id, "Category", "C_" + str(X_test[graph_id][2])) +graphs_test.encode() +print("Testing data produced") + +tm = MultiClassGraphTsetlinMachine( + args.number_of_clauses, + args.T, + args.s, + number_of_state_bits = args.number_of_state_bits, + depth=args.depth, + message_size=args.message_size, + message_bits=args.message_bits, + max_included_literals=args.max_included_literals, + double_hashing = args.double_hashing +) + +for i in range(args.epochs): + start_training = time() + tm.fit(graphs_train, Y_train, epochs=1, incremental=True) + stop_training = time() + + start_testing = time() + result_test = 100*(tm.predict(graphs_test) == Y_test).mean() + stop_testing = time() + + result_train = 100*(tm.predict(graphs_train) == Y_train).mean() + print("%d %.2f %.2f %.2f %.2f" % (i, result_train, result_test, stop_training-start_training, stop_testing-start_testing)) + +# weights = tm.get_state()[1].reshape(2, -1) +# for i in range(tm.number_of_clauses): +# print("Clause #%d W:(%d %d)" % (i, weights[0,i], weights[1,i]), end=' ') +# l = [] +# for k in range(args.hypervector_size * 2): +# if tm.ta_action(0, i, k): +# if k < args.hypervector_size: +# l.append("x%d" % (k)) +# else: +# l.append("NOT x%d" % (k - args.hypervector_size)) + +# for k in range(args.message_size * 2): +# if tm.ta_action(1, i, k): +# if k < args.message_size: +# l.append("c%d" % (k)) +# else: +# l.append("NOT c%d" % (k - args.message_size)) + +# print(" AND ".join(l)) + +# print(graphs_test.hypervectors) +# print(tm.hypervectors) +# print(graphs_test.edge_type_id) \ No newline at end of file diff --git a/examples/applications/recommendation/prepare_dataset.py b/examples/applications/recommendation/prepare_dataset.py new file mode 100644 index 00000000..582b569d --- /dev/null +++ b/examples/applications/recommendation/prepare_dataset.py @@ -0,0 +1,145 @@ +import pandas as pd +import kagglehub +import numpy as np + +np.random.seed(42) + +def amazon_products(): + print("Creating training data") + path = kagglehub.dataset_download("karkavelrajaj/amazon-sales-dataset") + print("Path to dataset files:", path) + data_file = path + "/amazon.csv" + org_data = pd.read_csv(data_file) + print("Original data shape:", org_data.shape) + return org_data[['product_id', 'category', 'user_id', 'rating']] + +def aug_amazon_products(): + org_data = amazon_products() + org_data['rating'] = pd.to_numeric(org_data['rating'], errors='coerce') # Coerce invalid values to NaN + org_data.dropna(subset=['rating'], inplace=True) # Drop rows with NaN ratings + org_data['rating'] = org_data['rating'].astype(int) + # Expand the dataset 10 times + data = pd.concat([org_data] * 10, ignore_index=True) + # Shuffle the expanded dataset + data = data.sample(frac=1, random_state=42).reset_index(drop=True) + # Add noise + # Define the noise ratio + noise_ratio = 0.1 # 10% noise + # Select rows to apply noise + num_noisy_rows = int(noise_ratio * len(data)) + noisy_indices = np.random.choice(data.index, size=num_noisy_rows, replace=False) + # Add noise to ratings + data.loc[noisy_indices, 'rating'] = np.random.choice(range(1, 6), size=num_noisy_rows) + # Add noise to categories + unique_categories = data['category'].unique() + data.loc[noisy_indices, 'category'] = np.random.choice(unique_categories, size=num_noisy_rows) + # Print a preview of the noisy and expanded dataset + print("Expanded data shape:", data.shape) + print("Data preview:\n", data.head()) + return data + +def artificial(): + num_users = 5 # Number of unique users + num_items =10 # Number of unique items + num_categories = 5 # Number of unique categories + num_interactions = 1000 # Number of user-item interactions + # Generate random ratings (e.g., between 1 and 5) + ratings = np.random.choice(range(1, 3), num_interactions) + # Generate random user-item interactions + user_ids = np.random.choice(range(num_users), num_interactions) + item_ids = np.random.choice(range(num_items), num_interactions) + categories = np.random.choice(range(num_categories), num_interactions) + + data = pd.DataFrame({ + 'user_id': user_ids, + 'product_id': item_ids, + 'category': categories, + 'rating': ratings + }) + return data + +def artificial_with_user_pref(): + num_users = 100 # Number of unique users + num_items = 50 # Number of unique items + num_categories = 50 # Number of unique categories + num_interactions = 1000 # Number of user-item interactions + noise_ratio = 0.01 # Percentage of noisy interactions + + # Generate user preferences: each user prefers 1-3 random categories + user_preferences = { + user: np.random.choice(range(num_categories), size=np.random.randint(1, 4), replace=False) + for user in range(num_users) + } + + # Assign each item to a category + item_categories = {item: np.random.choice(range(num_categories)) for item in range(num_items)} + + # Generate interactions + user_ids = np.random.choice(range(num_users), num_interactions) + item_ids = np.random.choice(range(num_items), num_interactions) + + # Generate ratings based on the pattern + ratings = [] + for user, item in zip(user_ids, item_ids): + item_category = item_categories[item] + if item_category in user_preferences[user]: + ratings.append(np.random.choice([3, 4])) # High rating for preferred categories + else: + ratings.append(np.random.choice([1, 2])) # Low rating otherwise + + # Introduce noise + num_noisy = int(noise_ratio * num_interactions) + noisy_indices = np.random.choice(range(num_interactions), num_noisy, replace=False) + for idx in noisy_indices: + ratings[idx] = np.random.choice(range(1, 6)) # Replace with random rating + + # Combine into a DataFrame + data = pd.DataFrame({ + 'user_id': user_ids, + 'product_id': item_ids, + 'category': [item_categories[item] for item in item_ids], + 'rating': ratings + }) + return data + +def artificial_pattered(): + num_users = 100 # Number of unique users + num_items = 50 # Number of unique items + num_categories = 5 # Number of unique categories + num_interactions = 10000 # Number of user-item interactions + noise_ratio = 0.01 # Percentage of noisy interactions + + # Step 1: Define deterministic user preferences + user_preferences = {user: user % num_categories for user in range(num_users)} + + # Step 2: Assign items to categories in a cyclic pattern + item_categories = {item: item % num_categories for item in range(num_items)} + + # Step 3: Generate deterministic interactions + user_ids = np.arange(num_interactions) % num_users # Cycle through users + item_ids = np.arange(num_interactions) % num_items # Cycle through items + + # Step 4: Generate ratings based on the pattern + ratings = [] + for user, item in zip(user_ids, item_ids): + preferred_category = user_preferences[user] + item_category = item_categories[item] + if item_category == preferred_category: + ratings.append(5) # High rating for preferred category + else: + ratings.append(1) # Low rating otherwise + + # Step 5: Introduce noise + num_noisy = int(noise_ratio * num_interactions) + noisy_indices = np.random.choice(range(num_interactions), num_noisy, replace=False) + for idx in noisy_indices: + ratings[idx] = np.random.choice(range(1, 6)) # Replace with random rating + + # Step 6: Create a DataFrame + data = pd.DataFrame({ + 'user_id': user_ids, + 'product_id': item_ids, + 'category': [item_categories[item] for item in item_ids], + 'rating': ratings + }) + return data \ No newline at end of file From 801b7e399fd3050920f2b1d988dad94ac6925c88 Mon Sep 17 00:00:00 2001 From: Ahmed Khalid Date: Fri, 20 Dec 2024 13:27:26 +0000 Subject: [PATCH 16/33] update --- .../__pycache__/prepare_dataset.cpython-310.pyc | Bin 0 -> 4044 bytes .../main.py} | 13 +++++++++---- .../prepare_dataset.py | 8 +++++--- .../{ => products_recommendation}/test.ipynb | 0 .../__pycache__/prepare_dataset.cpython-310.pyc | Bin 1415 -> 0 bytes 5 files changed, 14 insertions(+), 7 deletions(-) create mode 100644 examples/applications/products_recommendation/__pycache__/prepare_dataset.cpython-310.pyc rename examples/applications/{recommendation/main_products.py => products_recommendation/main.py} (94%) rename examples/applications/{recommendation => products_recommendation}/prepare_dataset.py (97%) rename examples/applications/{ => products_recommendation}/test.ipynb (100%) delete mode 100644 examples/applications/recommendation/__pycache__/prepare_dataset.cpython-310.pyc diff --git a/examples/applications/products_recommendation/__pycache__/prepare_dataset.cpython-310.pyc b/examples/applications/products_recommendation/__pycache__/prepare_dataset.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d6947f000940c5e0a9189502c8ece11503498ecf GIT binary patch literal 4044 zcma)9&2uA16`$_;&}j5w*%rzgieLg%5LLFRP!zB+yMf(xQhay=36Pj9Vxn%#qm^g0 zJ>y+ljJSBKk{nzGl`99En=ko;`bKd8U*SekNq(*tXIX!zm)Q@;%PV-&yAZ+>Y+!9M^UJrm`L(wk!G->T>N^%6D9nX zgfGhY^+g~m_$>?ehSjc~VJ2&{OY;0HN`_e!_m(piMlm%+n1!Pk9){{+ z_*f29*bn<_;chre;*~TU$aIAaQkjifuZP*ra+a78P%Ljp=)O6sOw3oi>EluJzKVKL z91e`ta=H@^<;}LEgP}^qUN`GRLi^n?lf6V8=+a&)6{$RBylQ*e9jYkKbopV}>kZ`2 z-n+UnaiAlTC-EQ&h2}#{f6{PoC{c9? zp9T1mtm3QYp}Le=GIa5n%>E0F%<5B4kvC-5KD3Zor|%-CdyV9Y@}pmaeLc5DP1KLIm|G3$f0k*8`f{+K=?NK#Kb&&m_M<`?qs}s$!eag zvIVh#QHutRg!skHvu`1CMf;$=ZX#7zwwd;%Qi)36Y;*0Ud+%bWbt#I6gRm>ls3>7l zje2o{OhmDe`)7=lTszw;?CPotW0CB3(hSgosUuV}{Kp@fAHDjw(bL--r?5+-k<=w5 z(@aU3XfyQ{{3xomQS;vZ5OYXz3Ajzo;~}AwX2%>-3X@e;#uy!BNhjXhl`86Lk5-IB z?S*M}FqGOR6eGMC_(DVmak`=;0xJU0_CUt4*h!+U)UF9iyI84*!Gi=d?#0ogJ*mi5 zRe@-Gia>_o0BK!`lPHxPDx5^u;YNoP2OX6>Np-DgV^U0>>iJ@-&ZO`nnVNbiM1$sB z4EK7SXJAbwlRghoWgcs>23ufF-r|nk;7zv7tNe?s${nV@2a`)Mr_CwhF);rl8nZAL zd<PF$z06IfY%$k=!Dc zq@h%j#_L&QB#%;e$nRPYzVQwp`{VNaR$gAR#z7wBPVO&Rk1h3fUOFxk?|_wp9?n8y z&PTUr^c7K^>a$XA=iG2y#c&j@OMt0NSgx8^v0`mpGcluAREX`ss-?WPZav!1{Nplt z_MU~98)E*cpL-NpUJ?riV)eXkSgnAVz#g8NRQ&)g^<{`IG1d8pAV%O>#FMT20q#25CF{ULwEv5r%qmf*Ufs5aZ~3`#EmO0Yuh#~GN9uYOxQ z1g=x2ZezqOe%8Sc8IHT*tqt^~K7?m+zWj;z$!KK?uFjJv+cD_InJ2eb*TGNWAicFZ z9sijq2~j3W^3p_EIgj#q66FaiqB0&Kv}w6r1aivNPcdFQR1@mwXinW%n|S>ho|Ja= zc}v}-?$=0|(kAT&f?g)gS0O&*BW)x3`0pk}k22wn8E}XD(x1(K7mJyyyAP97rsqs? z&eWypG#nbN52Cc#O-Yl|`c3Qe?c>Er^)g1agPFZB)UyVy;U8^}BAiILy5NSQsOk+F zsdXAQr+!F^TO?i~@f`@=Fw8=UlS;-o57dvT?+%F{k)S;)w+(j91xca;_@cMg^ReMD3sX@ag`_98REi)nux$^nG4S3jp6NC z!unD{SOraug9&j}@=6{&w z&;fe!*P%8cr#i@~pOue;3v#N9xfyb57;>7+=Rg|s#rdbG#?`ANz7G*KUPS9khN6Sg z)J2Qzs4hxWyE$s3WzE!VQ64{`8LueK6nawxEo=1R3^H&{#{nqC=c18wSJdxm6;o_R z+`neg$D|KQ5C?3iUsCgb7QcFfOrDwf%?llYO#K?-Pt^Dg#QzKCnOd$~Rm-SgbJbIC z(X4NiFtu!mj}C~Upc_{ewA!NnYioF+sB~=b`F-y|{gy_&LxSp9Js>f?Q2dTwO#v@n zk)}R+Qv%Fv>9*{aUAOC#vh_{rRjU)wMHOa2$CE1v?NWTI;T literal 0 HcmV?d00001 diff --git a/examples/applications/recommendation/main_products.py b/examples/applications/products_recommendation/main.py similarity index 94% rename from examples/applications/recommendation/main_products.py rename to examples/applications/products_recommendation/main.py index 03d5b641..e045607a 100644 --- a/examples/applications/recommendation/main_products.py +++ b/examples/applications/products_recommendation/main.py @@ -10,19 +10,19 @@ def default_args(**kwargs): parser = argparse.ArgumentParser() - parser.add_argument("--epochs", default=10, type=int) + parser.add_argument("--epochs", default=100, type=int) parser.add_argument("--number-of-clauses", default=1000, type=int) parser.add_argument("--T", default=10000, type=int) parser.add_argument("--s", default=10.0, type=float) parser.add_argument("--number-of-state-bits", default=8, type=int) - parser.add_argument("--depth", default=3, type=int) + parser.add_argument("--depth", default=1, type=int) parser.add_argument("--hypervector-size", default=4096, type=int) parser.add_argument("--hypervector-bits", default=256, type=int) parser.add_argument("--message-size", default=256, type=int) parser.add_argument("--message-bits", default=2, type=int) parser.add_argument('--double-hashing', dest='double_hashing', default=False, action='store_true') parser.add_argument("--noise", default=0.01, type=float) - parser.add_argument("--max-included-literals", default=10, type=int) + parser.add_argument("--max-included-literals", default=3, type=int) args = parser.parse_args() for key, value in kwargs.items(): @@ -30,9 +30,14 @@ def default_args(**kwargs): setattr(args, key, value) return args args = default_args() +np.random.seed(42) +# data = prepare_dataset.amazon_products() data = prepare_dataset.aug_amazon_products() -print(data.head()) +# data = prepare_dataset.artificial() +# data = prepare_dataset.artificial_with_user_pref() +# data = prepare_dataset.artificial_pattered() +# print(data.head()) le_user = LabelEncoder() le_item = LabelEncoder() le_category = LabelEncoder() diff --git a/examples/applications/recommendation/prepare_dataset.py b/examples/applications/products_recommendation/prepare_dataset.py similarity index 97% rename from examples/applications/recommendation/prepare_dataset.py rename to examples/applications/products_recommendation/prepare_dataset.py index 582b569d..20162f01 100644 --- a/examples/applications/recommendation/prepare_dataset.py +++ b/examples/applications/products_recommendation/prepare_dataset.py @@ -2,7 +2,6 @@ import kagglehub import numpy as np -np.random.seed(42) def amazon_products(): print("Creating training data") @@ -14,6 +13,7 @@ def amazon_products(): return org_data[['product_id', 'category', 'user_id', 'rating']] def aug_amazon_products(): + np.random.seed(42) org_data = amazon_products() org_data['rating'] = pd.to_numeric(org_data['rating'], errors='coerce') # Coerce invalid values to NaN org_data.dropna(subset=['rating'], inplace=True) # Drop rows with NaN ratings @@ -24,7 +24,7 @@ def aug_amazon_products(): data = data.sample(frac=1, random_state=42).reset_index(drop=True) # Add noise # Define the noise ratio - noise_ratio = 0.1 # 10% noise + noise_ratio = 0.01 # 10% noise # Select rows to apply noise num_noisy_rows = int(noise_ratio * len(data)) noisy_indices = np.random.choice(data.index, size=num_noisy_rows, replace=False) @@ -35,10 +35,10 @@ def aug_amazon_products(): data.loc[noisy_indices, 'category'] = np.random.choice(unique_categories, size=num_noisy_rows) # Print a preview of the noisy and expanded dataset print("Expanded data shape:", data.shape) - print("Data preview:\n", data.head()) return data def artificial(): + np.random.seed(42) num_users = 5 # Number of unique users num_items =10 # Number of unique items num_categories = 5 # Number of unique categories @@ -59,6 +59,7 @@ def artificial(): return data def artificial_with_user_pref(): + np.random.seed(42) num_users = 100 # Number of unique users num_items = 50 # Number of unique items num_categories = 50 # Number of unique categories @@ -103,6 +104,7 @@ def artificial_with_user_pref(): return data def artificial_pattered(): + np.random.seed(42) num_users = 100 # Number of unique users num_items = 50 # Number of unique items num_categories = 5 # Number of unique categories diff --git a/examples/applications/test.ipynb b/examples/applications/products_recommendation/test.ipynb similarity index 100% rename from examples/applications/test.ipynb rename to examples/applications/products_recommendation/test.ipynb diff --git a/examples/applications/recommendation/__pycache__/prepare_dataset.cpython-310.pyc b/examples/applications/recommendation/__pycache__/prepare_dataset.cpython-310.pyc deleted file mode 100644 index 334918248b0038345364fc10337498871cad89c2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1415 zcmZ8hOK&SR7_~i*q)D2lZBzsjC}Kqr_jbjmssQQ2T^1yekh&T9j(w+-PR4`n^bsYi zvds^GRm+ZFfcPukvg)1{E6z75QZKf~kI%>7^LWheZjYh;@uMy-Q^x+H&GRO(`3j%= z41;2dSL~d(vp5&+9G%BXsOV>Qo~T$Qm{Y~SXVdf%H`rND<9{u}g1%Sy+-nRT8t*t~ zp5F^49{8Fh(Td#(&Q?st@3A%a(S5w)I1@NaR$`X4r=nmg{f)oRV5@VsJbGod^krR4 zyp?553njf=etaqIrM%LWl`}azmUFo@^$RB}?OqVTX}>)BPWp?9H|-4wCT~j|pDg=N z@Rzx}T8_W9Wl`3$YEct+Asc-%jYF@oMlEuml`3>|>2+c3ZRjkVwj@ez>(w+3Nn^{} zhn-7V6qUYMya}Tx2Qp=@>&i$KL<1!VL}f4;lGeyH3ULDm!w$hS@-1|YEi&5v&g>rY z_|NIFY?@rM|IVX>TSfV3u%AD*IW%i2av(4<1 zA|BRNr`sR^C`1mQEh!DTEG7keQYh~t{^WYTlv9hKqe*J~opI^QvoGX_)bWqM#FV$ko z?T45wJ+kHao0|qYw0h3iGdfsI9Vy*Pe!$1#NW{@djQKb3mq@kWxOSYm;R= qq%?t7&8__e>unwh`OGpIu&w#-=^X!wN`FD6c0__Mo)@|p^!^25{fROF From fdfb81fb27cab73377e095984cdd5bede8002401 Mon Sep 17 00:00:00 2001 From: Ahmed Khalid Date: Tue, 24 Dec 2024 11:30:06 +0000 Subject: [PATCH 17/33] add TMClassifier --- .../prepare_dataset.cpython-310.pyc | Bin 4044 -> 4044 bytes .../products_recommendation/baseline.py | 114 ++++++++++++++++++ 2 files changed, 114 insertions(+) create mode 100644 examples/applications/products_recommendation/baseline.py diff --git a/examples/applications/products_recommendation/__pycache__/prepare_dataset.cpython-310.pyc b/examples/applications/products_recommendation/__pycache__/prepare_dataset.cpython-310.pyc index d6947f000940c5e0a9189502c8ece11503498ecf..7742db56bf6d6b0f6731787e01a26b4ff39baead 100644 GIT binary patch delta 19 ZcmX>je@31wpO=@50SKatH*y`~2LLh>1kL~e delta 19 ZcmX>je@31wpO=@50SF@UH*y`~2LLhb1jhgX diff --git a/examples/applications/products_recommendation/baseline.py b/examples/applications/products_recommendation/baseline.py new file mode 100644 index 00000000..f1d37271 --- /dev/null +++ b/examples/applications/products_recommendation/baseline.py @@ -0,0 +1,114 @@ +import logging +import argparse +import numpy as np +from tmu.models.classification.vanilla_classifier import TMClassifier +from tmu.tools import BenchmarkTimer +from tmu.util.cuda_profiler import CudaProfiler +from sklearn.model_selection import train_test_split +from sklearn.preprocessing import LabelEncoder +import prepare_dataset +from tmu.data import MNIST +from sklearn.preprocessing import OneHotEncoder +import pandas as pd + +_LOGGER = logging.getLogger(__name__) + +def metrics(args): + return dict( + accuracy=[], + train_time=[], + test_time=[], + args=vars(args) + ) + +def prepare_data(): + # Step 1: Load and encode dataset + data = prepare_dataset.aug_amazon_products() + le_user = LabelEncoder() + le_item = LabelEncoder() + le_category = LabelEncoder() + le_rating = LabelEncoder() + data['user_id'] = le_user.fit_transform(data['user_id']) + data['product_id'] = le_item.fit_transform(data['product_id']) + data['category'] = le_category.fit_transform(data['category']) + data['rating'] = le_rating.fit_transform(data['rating']) + + x = data[['user_id', 'product_id', 'category']].values + y = data['rating'].values + # Step 3: One-hot encode features + encoder = OneHotEncoder(sparse_output=False, dtype=np.uint32) + x_binary = encoder.fit_transform(x) + + # Verify feature dimensions + print(f"Number of features after one-hot encoding: {x_binary.shape[1]}") + + x_train, x_test, y_train, y_test = train_test_split(x_binary, y, test_size=0.2, random_state=42) + + y_train = y_train.astype(np.uint32) + y_test = y_test.astype(np.uint32) + + print("x_train shape:", x_train.shape, "dtype:", x_train.dtype) + print("y_train shape:", y_train.shape, "dtype:", y_train.dtype) + print("x_test shape:", x_test.shape, "dtype:", x_test.dtype) + print("y_test shape:", y_test.shape, "dtype:", y_test.dtype) + + return x_train, x_test, y_train, y_test + +def main(args): + experiment_results = metrics(args) + X_train, X_test, Y_train, Y_test = prepare_data() + + tm = TMClassifier( + number_of_clauses=args.num_clauses, + T=args.T, + s=args.s, + max_included_literals=args.max_included_literals, + platform=args.platform, + weighted_clauses=args.weighted_clauses + ) + _LOGGER.info(f"Running {TMClassifier} for {args.epochs}") + for epoch in range(args.epochs): + benchmark_total = BenchmarkTimer(logger=None, text="Epoch Time") + with benchmark_total: + benchmark1 = BenchmarkTimer(logger=None, text="Training Time") + with benchmark1: + res = tm.fit( + X_train, + Y_train, + ) + + experiment_results["train_time"].append(benchmark1.elapsed()) + benchmark2 = BenchmarkTimer(logger=None, text="Testing Time") + with benchmark2: + result = 100 * (tm.predict(X_test) == Y_test).mean() + experiment_results["accuracy"].append(result) + experiment_results["test_time"].append(benchmark2.elapsed()) + + _LOGGER.info(f"Epoch: {epoch + 1}, Accuracy: {result:.2f}, Training Time: {benchmark1.elapsed():.2f}s, " + f"Testing Time: {benchmark2.elapsed():.2f}s") + + if args.platform == "CUDA": + CudaProfiler().print_timings(benchmark=benchmark_total) + + return experiment_results + + +def default_args(**kwargs): + parser = argparse.ArgumentParser() + parser.add_argument("--num_clauses", default=2000, type=int) + parser.add_argument("--T", default=5000, type=int) + parser.add_argument("--s", default=10.0, type=float) + parser.add_argument("--max_included_literals", default=32, type=int) + parser.add_argument("--platform", default="CPU_sparse", type=str, choices=["CPU", "CPU_sparse", "CUDA"]) + parser.add_argument("--weighted_clauses", default=True, type=bool) + parser.add_argument("--epochs", default=60, type=int) + args = parser.parse_args() + for key, value in kwargs.items(): + if key in args.__dict__: + setattr(args, key, value) + return args + + +if __name__ == "__main__": + results = main(default_args()) + _LOGGER.info(results) \ No newline at end of file From 3168dc7c889fafd0adada91d73b99dd983d64a5b Mon Sep 17 00:00:00 2001 From: Ahmed Khalid Date: Tue, 24 Dec 2024 17:12:57 +0000 Subject: [PATCH 18/33] update --- .../prepare_dataset.cpython-310.pyc | Bin 4044 -> 5371 bytes .../products_recommendation/baseline.py | 49 ++------------- .../products_recommendation/main.py | 59 ++++++------------ .../prepare_dataset.py | 44 +++++++++++-- 4 files changed, 64 insertions(+), 88 deletions(-) diff --git a/examples/applications/products_recommendation/__pycache__/prepare_dataset.cpython-310.pyc b/examples/applications/products_recommendation/__pycache__/prepare_dataset.cpython-310.pyc index 7742db56bf6d6b0f6731787e01a26b4ff39baead..a91b323f497e840523e521b9b11aa792ba20e8ba 100644 GIT binary patch literal 5371 zcma)A-ESOM6`y-&c4qd|>yK;_3dIykS*kcy5eTIwDKs$!iu(z)re&Gz9j|Azp4r?P zr}i?7P#vl5gC!6G3B<#uZ{dOegMZ<^A&|f?yzo{b%I}<6+w0VU?rP4QIrn4kJs-b& z&Ls7E#lr99zx8|3XDsWl^f3Fg@$f9J^c@goDKW4%1mER`J)W;rp z+x~{nyaT?xQ3hX9?p2=!UyQ@&lWZ#J z($J$HZH%txaHVS?88){sgq4YjRPvq}lf=%geJQNmQqFm6EHZoI5}a z(YKM?TZaja6_#VoxawV#!Yu_LfyuT;^83ZhKB?O6h~1nPGF8G zR^jd;=@gxvO&xSiO$V_`wmWGC|3X(s>14QeAI)hr3XKOt@=T=sw$P8^rimXd-Pj#s zXsAv(?UIr4kUWzn#T;R%*rK|IF{Y9wop@(E)KS;C^rAR0Zjfes!_e5|!Pr#{-@-=N z+e}r5*iW%;V-La@J+_jl8=4aD#gy=-9tIB*%(xRrw|7EarZtjhAu_6Q5~X2>A|=uD zv<8QK+cdC@4z=xdbnIIKUF?nj{i>W%(po_wkW1%36G|ghL)9XCo(lmeiX^@(5 zMN2frf>@F**|e9$vaHEZiJEkTzJd;2T*p6JuZN5M`sZL~{aVQ}e0dCy9ZTgD{x-2Q zhkTiQ>z*w9jbs2f!4mc=mH+}-;_{r-2cF9kFDp&lyp+4S2fy_G-~a~w+=rj}>$-(i z98#?5M~M4}4GzbNbqL0~?`T@C=63Q0_Q6p^Qiu<^^+q4+f&Y=`Z# zDU<#Q$kKw1vT!>sta74r)1+STix8g5fCGPI%u9_wML}>h7I>JN%E?guHF_`M*Nn|7 zfzG1Ms2c@?FQFw}2B`^W`d^X@aseiDq!h*9|F`4n=h5kux#l3C{gW!vrx8qNn#+D) zz+6(5#`e2nQz%z?cm0gcA90<4!(pjEmBLIUeZQE?Df;CS#w|=q5;7%8+@3Xt@yK-h z^1Aib<8R8bKQ6y#<>iakxRO_LC-*N}cP#xz?oA5AJAk4}52qkOLl|j8;`ubNiSVPE=pDZtrIPq)aP&*TSCZ;P-Qv zc9wf;fnBVTH{fE6Qx{Xv;UT-oDmZ-(;h-B{T|-MsKT}cr(L+YvI=wx2I`kf6~r_dS;%mq4r81K`UbU;#r1U}FA{kb#MEbZw^tw;A0+zg)NDs;_XLPw zo7ZV1I~8l-;zM1AOq0?08Y=9IlAL-HsaY5GX7WfO05o|SaF>u|) z@KeCmgy0ysS`Py*2f$SVaJf_9@|1rDaFwUh8Q?-HO+a9^IIEdH2|PUrSdSEdRlwA^ zG6k+`Ud=1Fq<)#eN#QgCP(8zZrQm14sm8%ia7y4*0|?asPW8Ob;M8C^Kr1Se`V^cR z0H=Odo>Y#(iK>$maB4C*&E<0djrjtc^mm{y{aqr@fJ9Ar*4YSELNmt&9h)4x9>s2s z*l1ZZF)No|y3rB#Y?F$JO$ zRBEsU1rbZA_^?@GPbD@a4ojS=#DN6pu1Zt^_mP26wagr+i<@+=y6b1Afc68@wykX} z*VH$otb^h?PB#<1ZQMJ-U?)tGF_Kb^HwgK#WQGvcR3Vw389-G$6j`4UyGHEI_#PXf zj+!COG}XzwoxR^+#vh-*1+EL?mQc)C7>;DfMd#9d9b+qH-$We10%_oo0G;M0O_R%p35)k7}r#5ae zhw3a3?b*MTO|7v3pwYE{m^djqQx; zXgtxnCd_V^H3cWb7HUvrkzaXQZ;^!5qq$^1E=l!>M);f~ zAEX70R?uWhD7%Jx#fHwOw+3OLTyPG+8)Jh+Gbb@3l#oIaP3#HH+ zxZ>qHO5dbM!Z(h?LP4~enGNxw!h!9t;+;E#@G4T3qOSJwv52xB`unm{_Rst0{7e3m Ie!b@Z7rInPIHVq@1toDl+x+w=mn^r}&EZBpw$AR5#zPz(Mh2w9Ck z5|Zz8&O&XEwOOdwj24q(l1WYK9RYiZwOGxRqQa#i&5q#I2jfvHwDx&xH=)qPbc+!s zRb)i7I-<>6yk<+vTG|~^%aAwI;cYInZ4p{65oTL_j~CeWu5LNCtL8`Z!sD=(}zqB&#nY971<9I(c`?Mm6(@^%{aa#JyRoJXuf@p_j1Efcir8vvuuq zvMkmsDWvZjL(BA6AU(ahtky#kSS-98>)U~^x?G({oC9H^_4F4KrmG(op)1r|G#KmE z(BD#T59YyICAdm0K*`fgy$0uHK$0E2C0gwWJVz<(Vsy(sHa95s9UAEJIFH!nO)vB- z4OR7nCz*X`Us*j%6xSZgERpaeBBIJz+o?8V(`z>Ux>R#;rvXa@ZeA_JJ_lF|%?}3mg=0x9TZu9cvT=p!V!q%t~;1b|8 zLDhMYEbLD)x}Te!8B}@&l&95;e#*VO{0T+|L`d=sk$m^z_W+Q#q^`kyqN%l@uA~1{ zm>Yc`j=y@J;7b@cqlY8&yNR-y!;~dG>DW|!^#Rx)0up7DV>OW1c}89^*8zu0OapPD zsr);>Qof9q^Y-ip$R7b9z4{o?mtNgM*NN`t*F*dQt{t0AI?i5ju*WI##YTO5tNGbM v70C^83X?7Z$^c9p(j`$SxeTnOExG~j`R6z>0=O)?#Oc+nv^8msXXpO~)Wa`t diff --git a/examples/applications/products_recommendation/baseline.py b/examples/applications/products_recommendation/baseline.py index f1d37271..b390764a 100644 --- a/examples/applications/products_recommendation/baseline.py +++ b/examples/applications/products_recommendation/baseline.py @@ -1,15 +1,9 @@ import logging import argparse -import numpy as np from tmu.models.classification.vanilla_classifier import TMClassifier from tmu.tools import BenchmarkTimer from tmu.util.cuda_profiler import CudaProfiler -from sklearn.model_selection import train_test_split -from sklearn.preprocessing import LabelEncoder import prepare_dataset -from tmu.data import MNIST -from sklearn.preprocessing import OneHotEncoder -import pandas as pd _LOGGER = logging.getLogger(__name__) @@ -21,42 +15,11 @@ def metrics(args): args=vars(args) ) -def prepare_data(): - # Step 1: Load and encode dataset - data = prepare_dataset.aug_amazon_products() - le_user = LabelEncoder() - le_item = LabelEncoder() - le_category = LabelEncoder() - le_rating = LabelEncoder() - data['user_id'] = le_user.fit_transform(data['user_id']) - data['product_id'] = le_item.fit_transform(data['product_id']) - data['category'] = le_category.fit_transform(data['category']) - data['rating'] = le_rating.fit_transform(data['rating']) - - x = data[['user_id', 'product_id', 'category']].values - y = data['rating'].values - # Step 3: One-hot encode features - encoder = OneHotEncoder(sparse_output=False, dtype=np.uint32) - x_binary = encoder.fit_transform(x) - - # Verify feature dimensions - print(f"Number of features after one-hot encoding: {x_binary.shape[1]}") - - x_train, x_test, y_train, y_test = train_test_split(x_binary, y, test_size=0.2, random_state=42) - - y_train = y_train.astype(np.uint32) - y_test = y_test.astype(np.uint32) - - print("x_train shape:", x_train.shape, "dtype:", x_train.dtype) - print("y_train shape:", y_train.shape, "dtype:", y_train.dtype) - print("x_test shape:", x_test.shape, "dtype:", x_test.dtype) - print("y_test shape:", y_test.shape, "dtype:", y_test.dtype) - - return x_train, x_test, y_train, y_test - def main(args): experiment_results = metrics(args) - X_train, X_test, Y_train, Y_test = prepare_data() + data = prepare_dataset.aug_amazon_products() + x, y = prepare_dataset.construct_x_y(data) + X_train, X_test, Y_train, Y_test = prepare_dataset.one_hot_encoding(x,y) tm = TMClassifier( number_of_clauses=args.num_clauses, @@ -92,23 +55,21 @@ def main(args): return experiment_results - def default_args(**kwargs): parser = argparse.ArgumentParser() parser.add_argument("--num_clauses", default=2000, type=int) - parser.add_argument("--T", default=5000, type=int) + parser.add_argument("--T", default=10000, type=int) parser.add_argument("--s", default=10.0, type=float) parser.add_argument("--max_included_literals", default=32, type=int) parser.add_argument("--platform", default="CPU_sparse", type=str, choices=["CPU", "CPU_sparse", "CUDA"]) parser.add_argument("--weighted_clauses", default=True, type=bool) - parser.add_argument("--epochs", default=60, type=int) + parser.add_argument("--epochs", default=10, type=int) args = parser.parse_args() for key, value in kwargs.items(): if key in args.__dict__: setattr(args, key, value) return args - if __name__ == "__main__": results = main(default_args()) _LOGGER.info(results) \ No newline at end of file diff --git a/examples/applications/products_recommendation/main.py b/examples/applications/products_recommendation/main.py index e045607a..41168a93 100644 --- a/examples/applications/products_recommendation/main.py +++ b/examples/applications/products_recommendation/main.py @@ -2,16 +2,13 @@ from GraphTsetlinMachine.tm import MultiClassGraphTsetlinMachine from time import time import argparse -import pandas as pd import numpy as np -from sklearn.model_selection import train_test_split -from sklearn.preprocessing import LabelEncoder import prepare_dataset def default_args(**kwargs): parser = argparse.ArgumentParser() - parser.add_argument("--epochs", default=100, type=int) - parser.add_argument("--number-of-clauses", default=1000, type=int) + parser.add_argument("--epochs", default=10, type=int) + parser.add_argument("--number-of-clauses", default=2000, type=int) parser.add_argument("--T", default=10000, type=int) parser.add_argument("--s", default=10.0, type=float) parser.add_argument("--number-of-state-bits", default=8, type=int) @@ -22,7 +19,7 @@ def default_args(**kwargs): parser.add_argument("--message-bits", default=2, type=int) parser.add_argument('--double-hashing', dest='double_hashing', default=False, action='store_true') parser.add_argument("--noise", default=0.01, type=float) - parser.add_argument("--max-included-literals", default=3, type=int) + parser.add_argument("--max-included-literals", default=23, type=int) args = parser.parse_args() for key, value in kwargs.items(): @@ -38,21 +35,8 @@ def default_args(**kwargs): # data = prepare_dataset.artificial_with_user_pref() # data = prepare_dataset.artificial_pattered() # print(data.head()) -le_user = LabelEncoder() -le_item = LabelEncoder() -le_category = LabelEncoder() -le_rating = LabelEncoder() -data['user_id'] = le_user.fit_transform(data['user_id']) -data['product_id'] = le_item.fit_transform(data['product_id']) -data['category'] = le_category.fit_transform(data['category']) -data['rating'] = le_rating.fit_transform(data['rating']) -x = data[['user_id', 'product_id', 'category']].values -y = data['rating'].values -X_train, X_test, Y_train, Y_test = train_test_split(x, y, test_size=0.2, random_state=42) -print("X_train shape:", X_train.shape) -print("y_train shape:", Y_train.shape) -print("X_test shape:", X_test.shape) -print("y_test shape:", Y_test.shape) +x, y = prepare_dataset.construct_x_y(data) +X_train, X_test, Y_train, Y_test = prepare_dataset.train_test_split(x,y) users = data['user_id'].unique() items = data['product_id'].unique() categories = data['category'].unique() @@ -160,24 +144,21 @@ def default_args(**kwargs): # weights = tm.get_state()[1].reshape(2, -1) # for i in range(tm.number_of_clauses): -# print("Clause #%d W:(%d %d)" % (i, weights[0,i], weights[1,i]), end=' ') -# l = [] -# for k in range(args.hypervector_size * 2): -# if tm.ta_action(0, i, k): -# if k < args.hypervector_size: -# l.append("x%d" % (k)) -# else: -# l.append("NOT x%d" % (k - args.hypervector_size)) - -# for k in range(args.message_size * 2): -# if tm.ta_action(1, i, k): -# if k < args.message_size: -# l.append("c%d" % (k)) -# else: -# l.append("NOT c%d" % (k - args.message_size)) - -# print(" AND ".join(l)) - +# print("Clause #%d W:(%d %d)" % (i, weights[0,i], weights[1,i]), end=' ') +# l = [] +# for k in range(args.hypervector_size * 2): +# if tm.ta_action(0, i, k): +# if k < args.hypervector_size: +# l.append("x%d" % (k)) +# else: +# l.append("NOT x%d" % (k - args.hypervector_size)) +# for k in range(args.message_size * 2): +# if tm.ta_action(1, i, k): +# if k < args.message_size: +# l.append("c%d" % (k)) +# else: +# l.append("NOT c%d" % (k - args.message_size)) +# print(" AND ".join(l)) # print(graphs_test.hypervectors) # print(tm.hypervectors) # print(graphs_test.edge_type_id) \ No newline at end of file diff --git a/examples/applications/products_recommendation/prepare_dataset.py b/examples/applications/products_recommendation/prepare_dataset.py index 20162f01..dfe1b50b 100644 --- a/examples/applications/products_recommendation/prepare_dataset.py +++ b/examples/applications/products_recommendation/prepare_dataset.py @@ -1,7 +1,9 @@ import pandas as pd import kagglehub import numpy as np - +from sklearn.model_selection import train_test_split +from sklearn.preprocessing import LabelEncoder +from sklearn.preprocessing import OneHotEncoder def amazon_products(): print("Creating training data") @@ -12,7 +14,7 @@ def amazon_products(): print("Original data shape:", org_data.shape) return org_data[['product_id', 'category', 'user_id', 'rating']] -def aug_amazon_products(): +def aug_amazon_products(noise_ratio = 0.01): np.random.seed(42) org_data = amazon_products() org_data['rating'] = pd.to_numeric(org_data['rating'], errors='coerce') # Coerce invalid values to NaN @@ -23,8 +25,6 @@ def aug_amazon_products(): # Shuffle the expanded dataset data = data.sample(frac=1, random_state=42).reset_index(drop=True) # Add noise - # Define the noise ratio - noise_ratio = 0.01 # 10% noise # Select rows to apply noise num_noisy_rows = int(noise_ratio * len(data)) noisy_indices = np.random.choice(data.index, size=num_noisy_rows, replace=False) @@ -144,4 +144,38 @@ def artificial_pattered(): 'category': [item_categories[item] for item in item_ids], 'rating': ratings }) - return data \ No newline at end of file + return data + +def construct_x_y(data): + le_user = LabelEncoder() + le_item = LabelEncoder() + le_category = LabelEncoder() + le_rating = LabelEncoder() + data['user_id'] = le_user.fit_transform(data['user_id']) + data['product_id'] = le_item.fit_transform(data['product_id']) + data['category'] = le_category.fit_transform(data['category']) + data['rating'] = le_rating.fit_transform(data['rating']) + x = data[['user_id', 'product_id', 'category']].values + y = data['rating'].values + return x,y + +def split_train_test(x,y): + X_train, X_test, Y_train, Y_test = train_test_split(x, y, test_size=0.2, random_state=42) + print("X_train shape:", X_train.shape) + print("y_train shape:", Y_train.shape) + print("X_test shape:", X_test.shape) + print("y_test shape:", Y_test.shape) + return X_train, X_test, Y_train, Y_test + +def one_hot_encoding(x,y): + encoder = OneHotEncoder(sparse_output=False, dtype=np.uint32) + x_binary = encoder.fit_transform(x) + # print(f"Number of features after one-hot encoding: {x_binary.shape[1]}") + x_train, x_test, y_train, y_test = split_train_test(x_binary, y) + y_train = y_train.astype(np.uint32) + y_test = y_test.astype(np.uint32) + print("x_train shape:", x_train.shape, "dtype:", x_train.dtype) + print("y_train shape:", y_train.shape, "dtype:", y_train.dtype) + print("x_test shape:", x_test.shape, "dtype:", x_test.dtype) + print("y_test shape:", y_test.shape, "dtype:", y_test.dtype) + return x_train, x_test, y_train, y_test \ No newline at end of file From e2232de44dfe505f19dfe0dffd298571cc485df3 Mon Sep 17 00:00:00 2001 From: Ahmed Khalid Date: Tue, 24 Dec 2024 18:54:16 +0000 Subject: [PATCH 19/33] add graph nn --- .../prepare_dataset.cpython-310.pyc | Bin 5371 -> 5371 bytes .../products_recommendation/graph_nn.py | 106 ++++++++++++++++++ .../{main.py => graph_tm.py} | 0 .../{baseline.py => tm_classifier.py} | 0 4 files changed, 106 insertions(+) create mode 100644 examples/applications/products_recommendation/graph_nn.py rename examples/applications/products_recommendation/{main.py => graph_tm.py} (100%) rename examples/applications/products_recommendation/{baseline.py => tm_classifier.py} (100%) diff --git a/examples/applications/products_recommendation/__pycache__/prepare_dataset.cpython-310.pyc b/examples/applications/products_recommendation/__pycache__/prepare_dataset.cpython-310.pyc index a91b323f497e840523e521b9b11aa792ba20e8ba..8a2cd18398677e0c9bd2a85d805bb970ed3cb003 100644 GIT binary patch delta 19 ZcmeyZ`CF4KpO=@50SLNYZ{+$Y0suYN1{(kX delta 19 ZcmeyZ`CF4KpO=@50SE%0Y~=bV0suT-1=|1s diff --git a/examples/applications/products_recommendation/graph_nn.py b/examples/applications/products_recommendation/graph_nn.py new file mode 100644 index 00000000..fa78480d --- /dev/null +++ b/examples/applications/products_recommendation/graph_nn.py @@ -0,0 +1,106 @@ +import torch +import torch.nn.functional as F +from torch_geometric.data import Data +from torch_geometric.nn import GCNConv +from time import time +import prepare_dataset + +# Step 1: Dataset Preparation + +data = prepare_dataset.aug_amazon_products() +x, y = prepare_dataset.construct_x_y(data) +X_train, X_test, Y_train, Y_test = prepare_dataset.train_test_split(x,y) + +# Graph Construction +num_users = len(data['user_id'].unique()) +num_items = len(data['product_id'].unique()) +num_categories = len(data['category'].unique()) +num_nodes = num_users + num_items + num_categories + +# Build edge list +edge_list = [] + +# User ↔ Item edges +for user, item in zip(X_train[:, 0], X_train[:, 1]): + edge_list.append((user, num_users + item)) # User to Item + edge_list.append((num_users + item, user)) # Item to User + +# Item ↔ Category edges +for item, category in zip(X_train[:, 1], X_train[:, 2]): + edge_list.append((num_users + item, num_users + num_items + category)) # Item to Category + edge_list.append((num_users + num_items + category, num_users + item)) # Category to Item + +# Create edge index for PyTorch Geometric +edge_index = torch.tensor(edge_list, dtype=torch.long).t() + +# Node features +node_features = torch.rand((num_nodes, 64), dtype=torch.float) + +# PyTorch Geometric Data object +graph_data = Data(x=node_features, edge_index=edge_index) + +# Step 2: Define GCN Model +class GCN(torch.nn.Module): + def __init__(self, input_dim, hidden_dim, output_dim): + super(GCN, self).__init__() + self.conv1 = GCNConv(input_dim, hidden_dim) + self.conv2 = GCNConv(hidden_dim, output_dim) + + def forward(self, x, edge_index): + x = self.conv1(x, edge_index) + x = F.relu(x) + x = self.conv2(x, edge_index) + return x + +# Initialize Model +model = GCN(input_dim=64, hidden_dim=128, output_dim=64) + +# Define optimizer +optimizer = torch.optim.Adam(model.parameters(), lr=0.01) + +# Convert train/test data to tensors +train_edges = torch.tensor( + [(user, num_users + item) for user, item in zip(X_train[:, 0], X_train[:, 1])], + dtype=torch.long +).t() +train_labels = torch.tensor(Y_train, dtype=torch.float) + +test_edges = torch.tensor( + [(user, num_users + item) for user, item in zip(X_test[:, 0], X_test[:, 1])], + dtype=torch.long +).t() +test_labels = torch.tensor(Y_test, dtype=torch.float) + +# Training Loop with Accuracy Logging +epochs = 1000 +for epoch in range(epochs): + start_time = time() + + # Training Phase + model.train() + optimizer.zero_grad() + out = model(graph_data.x, graph_data.edge_index) + + # User-item embeddings + user_embeddings = out[train_edges[0]] + item_embeddings = out[train_edges[1]] + predicted_ratings = (user_embeddings * item_embeddings).sum(dim=1) + + # Compute loss + loss = F.mse_loss(predicted_ratings, train_labels) + loss.backward() + optimizer.step() + + # Testing Phase + model.eval() + with torch.no_grad(): + out = model(graph_data.x, graph_data.edge_index) + test_user_embeddings = out[test_edges[0]] + test_item_embeddings = out[test_edges[1]] + test_predicted_ratings = (test_user_embeddings * test_item_embeddings).sum(dim=1) + + # Compute accuracy + test_accuracy = ((test_predicted_ratings.round() == test_labels).float().mean().item()) * 100 + + elapsed_time = time() - start_time + print(f"Epoch {epoch + 1}/{epochs}, Loss: {loss.item():.4f}, Accuracy: {test_accuracy:.2f}%, Time: {elapsed_time:.2f}s") diff --git a/examples/applications/products_recommendation/main.py b/examples/applications/products_recommendation/graph_tm.py similarity index 100% rename from examples/applications/products_recommendation/main.py rename to examples/applications/products_recommendation/graph_tm.py diff --git a/examples/applications/products_recommendation/baseline.py b/examples/applications/products_recommendation/tm_classifier.py similarity index 100% rename from examples/applications/products_recommendation/baseline.py rename to examples/applications/products_recommendation/tm_classifier.py From c4546310371c7dbcb0ccad959223c087c0a6669c Mon Sep 17 00:00:00 2001 From: Ahmed Khalid Date: Wed, 25 Dec 2024 13:04:24 +0000 Subject: [PATCH 20/33] add main.bash --- .../experiment_results.csv | 6 + .../products_recommendation/graph_nn.py | 226 +++++++------- .../products_recommendation/graph_tm.py | 277 +++++++++--------- .../products_recommendation/main.sh | 16 + .../products_recommendation/test.ipynb | 271 ----------------- .../products_recommendation/tm_classifier.py | 73 +++-- 6 files changed, 323 insertions(+), 546 deletions(-) create mode 100644 examples/applications/products_recommendation/experiment_results.csv create mode 100644 examples/applications/products_recommendation/main.sh delete mode 100644 examples/applications/products_recommendation/test.ipynb diff --git a/examples/applications/products_recommendation/experiment_results.csv b/examples/applications/products_recommendation/experiment_results.csv new file mode 100644 index 00000000..d3f66d27 --- /dev/null +++ b/examples/applications/products_recommendation/experiment_results.csv @@ -0,0 +1,6 @@ +Algorithm,Noise_Ratio,T,s,Max_Included_Literals,Epochs,Platform,Total_Time,Accuracy +Graph NN,0.005,0,0,0,1000,CPU,0.03006434440612793,76.72131061553955 +GraphTM,0.005,10000,10.0,23,10,CUDA,34.547648191452026,98.46994535519126 +TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,89.6943154335022,76.63934426229508 +Graph NN,0.01,0,0,0,1000,CPU,0.01817464828491211,75.95628499984741 +GraphTM,0.01,10000,10.0,23,10,CUDA,34.95576763153076,98.44262295081967 diff --git a/examples/applications/products_recommendation/graph_nn.py b/examples/applications/products_recommendation/graph_nn.py index fa78480d..30292db9 100644 --- a/examples/applications/products_recommendation/graph_nn.py +++ b/examples/applications/products_recommendation/graph_nn.py @@ -1,106 +1,130 @@ +import argparse import torch import torch.nn.functional as F from torch_geometric.data import Data from torch_geometric.nn import GCNConv -from time import time import prepare_dataset - -# Step 1: Dataset Preparation - -data = prepare_dataset.aug_amazon_products() -x, y = prepare_dataset.construct_x_y(data) -X_train, X_test, Y_train, Y_test = prepare_dataset.train_test_split(x,y) - -# Graph Construction -num_users = len(data['user_id'].unique()) -num_items = len(data['product_id'].unique()) -num_categories = len(data['category'].unique()) -num_nodes = num_users + num_items + num_categories - -# Build edge list -edge_list = [] - -# User ↔ Item edges -for user, item in zip(X_train[:, 0], X_train[:, 1]): - edge_list.append((user, num_users + item)) # User to Item - edge_list.append((num_users + item, user)) # Item to User - -# Item ↔ Category edges -for item, category in zip(X_train[:, 1], X_train[:, 2]): - edge_list.append((num_users + item, num_users + num_items + category)) # Item to Category - edge_list.append((num_users + num_items + category, num_users + item)) # Category to Item - -# Create edge index for PyTorch Geometric -edge_index = torch.tensor(edge_list, dtype=torch.long).t() - -# Node features -node_features = torch.rand((num_nodes, 64), dtype=torch.float) - -# PyTorch Geometric Data object -graph_data = Data(x=node_features, edge_index=edge_index) - -# Step 2: Define GCN Model -class GCN(torch.nn.Module): - def __init__(self, input_dim, hidden_dim, output_dim): - super(GCN, self).__init__() - self.conv1 = GCNConv(input_dim, hidden_dim) - self.conv2 = GCNConv(hidden_dim, output_dim) - - def forward(self, x, edge_index): - x = self.conv1(x, edge_index) - x = F.relu(x) - x = self.conv2(x, edge_index) - return x - -# Initialize Model -model = GCN(input_dim=64, hidden_dim=128, output_dim=64) - -# Define optimizer -optimizer = torch.optim.Adam(model.parameters(), lr=0.01) - -# Convert train/test data to tensors -train_edges = torch.tensor( - [(user, num_users + item) for user, item in zip(X_train[:, 0], X_train[:, 1])], - dtype=torch.long -).t() -train_labels = torch.tensor(Y_train, dtype=torch.float) - -test_edges = torch.tensor( - [(user, num_users + item) for user, item in zip(X_test[:, 0], X_test[:, 1])], - dtype=torch.long -).t() -test_labels = torch.tensor(Y_test, dtype=torch.float) - -# Training Loop with Accuracy Logging -epochs = 1000 -for epoch in range(epochs): - start_time = time() - - # Training Phase - model.train() - optimizer.zero_grad() - out = model(graph_data.x, graph_data.edge_index) - - # User-item embeddings - user_embeddings = out[train_edges[0]] - item_embeddings = out[train_edges[1]] - predicted_ratings = (user_embeddings * item_embeddings).sum(dim=1) - - # Compute loss - loss = F.mse_loss(predicted_ratings, train_labels) - loss.backward() - optimizer.step() - - # Testing Phase - model.eval() - with torch.no_grad(): - out = model(graph_data.x, graph_data.edge_index) - test_user_embeddings = out[test_edges[0]] - test_item_embeddings = out[test_edges[1]] - test_predicted_ratings = (test_user_embeddings * test_item_embeddings).sum(dim=1) - - # Compute accuracy - test_accuracy = ((test_predicted_ratings.round() == test_labels).float().mean().item()) * 100 - - elapsed_time = time() - start_time - print(f"Epoch {epoch + 1}/{epochs}, Loss: {loss.item():.4f}, Accuracy: {test_accuracy:.2f}%, Time: {elapsed_time:.2f}s") +from tmu.tools import BenchmarkTimer +import os +import pandas as pd + +def main(args): + results = [] + data = prepare_dataset.aug_amazon_products(noise_ratio = args.dataset_noise_ratio) + x, y = prepare_dataset.construct_x_y(data) + X_train, X_test, Y_train, Y_test = prepare_dataset.train_test_split(x,y) + # Graph Construction + num_users = len(data['user_id'].unique()) + num_items = len(data['product_id'].unique()) + num_categories = len(data['category'].unique()) + num_nodes = num_users + num_items + num_categories + # Build edge list + edge_list = [] + # User ↔ Item edges + for user, item in zip(X_train[:, 0], X_train[:, 1]): + edge_list.append((user, num_users + item)) # User to Item + edge_list.append((num_users + item, user)) # Item to User + # Item ↔ Category edges + for item, category in zip(X_train[:, 1], X_train[:, 2]): + edge_list.append((num_users + item, num_users + num_items + category)) # Item to Category + edge_list.append((num_users + num_items + category, num_users + item)) # Category to Item + # Create edge index for PyTorch Geometric + edge_index = torch.tensor(edge_list, dtype=torch.long).t() + # Node features + node_features = torch.rand((num_nodes, 64), dtype=torch.float) + # PyTorch Geometric Data object + graph_data = Data(x=node_features, edge_index=edge_index) + # Step 2: Define GCN Model + class GCN(torch.nn.Module): + def __init__(self, input_dim, hidden_dim, output_dim): + super(GCN, self).__init__() + self.conv1 = GCNConv(input_dim, hidden_dim) + self.conv2 = GCNConv(hidden_dim, output_dim) + def forward(self, x, edge_index): + x = self.conv1(x, edge_index) + x = F.relu(x) + x = self.conv2(x, edge_index) + return x + # Initialize Model + model = GCN(input_dim=64, hidden_dim=128, output_dim=64) + # Define optimizer + optimizer = torch.optim.Adam(model.parameters(), lr=0.01) + # Convert train/test data to tensors + train_edges = torch.tensor( + [(user, num_users + item) for user, item in zip(X_train[:, 0], X_train[:, 1])], + dtype=torch.long + ).t() + train_labels = torch.tensor(Y_train, dtype=torch.float) + test_edges = torch.tensor( + [(user, num_users + item) for user, item in zip(X_test[:, 0], X_test[:, 1])], + dtype=torch.long + ).t() + test_labels = torch.tensor(Y_test, dtype=torch.float) + # Training Loop with Accuracy Logging + for epoch in range(args.epochs): + benchmark_total = BenchmarkTimer(logger=None, text="Epoch Time") + with benchmark_total: + benchmark1 = BenchmarkTimer(logger=None, text="Training Time") + with benchmark1: + # Training Phase + model.train() + optimizer.zero_grad() + out = model(graph_data.x, graph_data.edge_index) + # User-item embeddings + user_embeddings = out[train_edges[0]] + item_embeddings = out[train_edges[1]] + predicted_ratings = (user_embeddings * item_embeddings).sum(dim=1) + # Compute loss + loss = F.mse_loss(predicted_ratings, train_labels) + loss.backward() + optimizer.step() + train_time = benchmark1.elapsed() + # Testing Phase + benchmark2 = BenchmarkTimer(logger=None, text="Testing Time") + with benchmark2: + model.eval() + with torch.no_grad(): + out = model(graph_data.x, graph_data.edge_index) + test_user_embeddings = out[test_edges[0]] + test_item_embeddings = out[test_edges[1]] + test_predicted_ratings = (test_user_embeddings * test_item_embeddings).sum(dim=1) + # Compute accuracy + accuracy = ((test_predicted_ratings.round() == test_labels).float().mean().item()) * 100 + test_time = benchmark2.elapsed() + total_time = benchmark_total.elapsed() + # Append results for each epoch + results.append({ + "Algorithm": "Graph NN", + "Noise_Ratio": args.dataset_noise_ratio, + "T": 0, + "s": 0, + "Max_Included_Literals": 0, + "Epochs": args.epochs, + "Platform": args.platform, + "Total_Time": total_time, + "Accuracy": accuracy, + }) + + # Save results to CSV + results_df = pd.DataFrame(results) + results_file = "experiment_results.csv" + if os.path.exists(results_file): + results_df.to_csv(results_file, mode='a', index=False, header=False) + else: + results_df.to_csv(results_file, index=False) + print(f"Results saved to {results_file}") + + +def default_args(**kwargs): + parser = argparse.ArgumentParser() + parser.add_argument("--platform", default="CPU", type=str, choices=["CPU", "CUDA"]) + parser.add_argument("--epochs", default=1000, type=int) + parser.add_argument("--dataset_noise_ratio", default=0.01, type=float) + args = parser.parse_args() + for key, value in kwargs.items(): + if key in args.__dict__: + setattr(args, key, value) + return args + +if __name__ == "__main__": + main(default_args()) \ No newline at end of file diff --git a/examples/applications/products_recommendation/graph_tm.py b/examples/applications/products_recommendation/graph_tm.py index 41168a93..0ec2171c 100644 --- a/examples/applications/products_recommendation/graph_tm.py +++ b/examples/applications/products_recommendation/graph_tm.py @@ -1,9 +1,145 @@ from GraphTsetlinMachine.graphs import Graphs from GraphTsetlinMachine.tm import MultiClassGraphTsetlinMachine -from time import time import argparse import numpy as np import prepare_dataset +import pandas as pd +from tmu.tools import BenchmarkTimer +import os + +def main(args): + np.random.seed(42) + results = [] + data = prepare_dataset.aug_amazon_products(noise_ratio = args.dataset_noise_ratio) + x, y = prepare_dataset.construct_x_y(data) + X_train, X_test, Y_train, Y_test = prepare_dataset.train_test_split(x,y) + users = data['user_id'].unique() + items = data['product_id'].unique() + categories = data['category'].unique() + # Initialize Graphs with symbols for GTM + number_of_nodes = 3 + symbols = [] + symbols = ["U_" + str(u) for u in users] + ["I_" + str(i) for i in items] + ["C_" + str(c) for c in categories] + print("Symbols: ",len(symbols)) + + # Train data + graphs_train = Graphs( + X_train.shape[0], + symbols=symbols, + hypervector_size=args.hypervector_size, + hypervector_bits=args.hypervector_bits, + double_hashing = args.double_hashing + ) + for graph_id in range(X_train.shape[0]): + graphs_train.set_number_of_graph_nodes(graph_id, number_of_nodes) + graphs_train.prepare_node_configuration() + for graph_id in range(X_train.shape[0]): + for node_id in range(graphs_train.number_of_graph_nodes[graph_id]): + number_of_edges = 2 if node_id > 0 and node_id < graphs_train.number_of_graph_nodes[graph_id]-1 else 1 + if node_id == 0: + graphs_train.add_graph_node(graph_id, "User", number_of_edges) + elif node_id == 1: + graphs_train.add_graph_node(graph_id, "Item", number_of_edges) + else: + graphs_train.add_graph_node(graph_id, "Category", number_of_edges) + graphs_train.prepare_edge_configuration() + for graph_id in range(X_train.shape[0]): + for node_id in range(graphs_train.number_of_graph_nodes[graph_id]): + if node_id == 0: + graphs_train.add_graph_node_edge(graph_id, "User", "Item", "UserItem") + + if node_id == 1: + graphs_train.add_graph_node_edge(graph_id, "Item", "Category", "ItemCategory") + graphs_train.add_graph_node_edge(graph_id, "Item", "User", "ItemUser") + + if node_id == 2: + graphs_train.add_graph_node_edge(graph_id, "Category", "Item", "CatrgoryItem") + + graphs_train.add_graph_node_property(graph_id, "User", "U_" + str(X_train[graph_id][0])) + graphs_train.add_graph_node_property(graph_id, "Item", "I_" + str(X_train[graph_id][1])) + graphs_train.add_graph_node_property(graph_id, "Category", "C_" + str(X_train[graph_id][2])) + graphs_train.encode() + print("Training data produced") + + # Test data + graphs_test = Graphs(X_test.shape[0], init_with=graphs_train) + for graph_id in range(X_test.shape[0]): + graphs_test.set_number_of_graph_nodes(graph_id, number_of_nodes) + graphs_test.prepare_node_configuration() + for graph_id in range(X_test.shape[0]): + for node_id in range(graphs_test.number_of_graph_nodes[graph_id]): + number_of_edges = 2 if node_id > 0 and node_id < graphs_test.number_of_graph_nodes[graph_id]-1 else 1 + if node_id == 0: + graphs_test.add_graph_node(graph_id, "User", number_of_edges) + elif node_id == 1: + graphs_test.add_graph_node(graph_id, "Item", number_of_edges) + else: + graphs_test.add_graph_node(graph_id, "Category", number_of_edges) + graphs_test.prepare_edge_configuration() + for graph_id in range(X_test.shape[0]): + for node_id in range(graphs_test.number_of_graph_nodes[graph_id]): + if node_id == 0: + graphs_test.add_graph_node_edge(graph_id, "User", "Item", "UserItem") + + if node_id == 1: + graphs_test.add_graph_node_edge(graph_id, "Item", "Category", "ItemCategory") + graphs_test.add_graph_node_edge(graph_id, "Item", "User", "ItemUser") + + if node_id == 2: + graphs_test.add_graph_node_edge(graph_id, "Category", "Item", "CatrgoryItem") + + graphs_test.add_graph_node_property(graph_id, "User", "U_" + str(X_test[graph_id][0])) + graphs_test.add_graph_node_property(graph_id, "Item", "I_" + str(X_test[graph_id][1])) + graphs_test.add_graph_node_property(graph_id, "Category", "C_" + str(X_test[graph_id][2])) + graphs_test.encode() + print("Testing data produced") + + tm = MultiClassGraphTsetlinMachine( + args.number_of_clauses, + args.T, + args.s, + number_of_state_bits = args.number_of_state_bits, + depth=args.depth, + message_size=args.message_size, + message_bits=args.message_bits, + max_included_literals=args.max_included_literals, + double_hashing = args.double_hashing + ) + + for epoch in range(args.epochs): + benchmark_total = BenchmarkTimer(logger=None, text="Epoch Time") + with benchmark_total: + benchmark1 = BenchmarkTimer(logger=None, text="Training Time") + with benchmark1: + tm.fit(graphs_train, Y_train, epochs=1, incremental=True) + train_time = benchmark1.elapsed() + + benchmark2 = BenchmarkTimer(logger=None, text="Testing Time") + with benchmark2: + accuracy = 100*(tm.predict(graphs_test) == Y_test).mean() + test_time = benchmark2.elapsed() + total_time = benchmark_total.elapsed() + # result_train = 100*(tm.predict(graphs_train) == Y_train).mean() + results.append({ + "Algorithm": "GraphTM", + "Noise_Ratio": args.dataset_noise_ratio, + "T": args.T, + "s": args.s, + "Max_Included_Literals": args.max_included_literals, + "Epochs": args.epochs, + "Platform": "CUDA", + "Total_Time": total_time, + "Accuracy": accuracy, + }) + + # Save results to CSV + results_df = pd.DataFrame(results) + results_file = "experiment_results.csv" + if os.path.exists(results_file): + results_df.to_csv(results_file, mode='a', index=False, header=False) + else: + results_df.to_csv(results_file, index=False) + print(f"Results saved to {results_file}") def default_args(**kwargs): parser = argparse.ArgumentParser() @@ -20,145 +156,12 @@ def default_args(**kwargs): parser.add_argument('--double-hashing', dest='double_hashing', default=False, action='store_true') parser.add_argument("--noise", default=0.01, type=float) parser.add_argument("--max-included-literals", default=23, type=int) - + parser.add_argument("--dataset_noise_ratio", default=0.01, type=float) args = parser.parse_args() for key, value in kwargs.items(): if key in args.__dict__: setattr(args, key, value) return args -args = default_args() -np.random.seed(42) - -# data = prepare_dataset.amazon_products() -data = prepare_dataset.aug_amazon_products() -# data = prepare_dataset.artificial() -# data = prepare_dataset.artificial_with_user_pref() -# data = prepare_dataset.artificial_pattered() -# print(data.head()) -x, y = prepare_dataset.construct_x_y(data) -X_train, X_test, Y_train, Y_test = prepare_dataset.train_test_split(x,y) -users = data['user_id'].unique() -items = data['product_id'].unique() -categories = data['category'].unique() -# Initialize Graphs with symbols for GTM -number_of_nodes = 3 -symbols = [] -symbols = ["U_" + str(u) for u in users] + ["I_" + str(i) for i in items] + ["C_" + str(c) for c in categories] -print("Symbols: ",len(symbols)) - -# Train data -graphs_train = Graphs( - X_train.shape[0], - symbols=symbols, - hypervector_size=args.hypervector_size, - hypervector_bits=args.hypervector_bits, - double_hashing = args.double_hashing -) -for graph_id in range(X_train.shape[0]): - graphs_train.set_number_of_graph_nodes(graph_id, number_of_nodes) -graphs_train.prepare_node_configuration() -for graph_id in range(X_train.shape[0]): - for node_id in range(graphs_train.number_of_graph_nodes[graph_id]): - number_of_edges = 2 if node_id > 0 and node_id < graphs_train.number_of_graph_nodes[graph_id]-1 else 1 - if node_id == 0: - graphs_train.add_graph_node(graph_id, "User", number_of_edges) - elif node_id == 1: - graphs_train.add_graph_node(graph_id, "Item", number_of_edges) - else: - graphs_train.add_graph_node(graph_id, "Category", number_of_edges) -graphs_train.prepare_edge_configuration() -for graph_id in range(X_train.shape[0]): - for node_id in range(graphs_train.number_of_graph_nodes[graph_id]): - if node_id == 0: - graphs_train.add_graph_node_edge(graph_id, "User", "Item", "UserItem") - - if node_id == 1: - graphs_train.add_graph_node_edge(graph_id, "Item", "Category", "ItemCategory") - graphs_train.add_graph_node_edge(graph_id, "Item", "User", "ItemUser") - - if node_id == 2: - graphs_train.add_graph_node_edge(graph_id, "Category", "Item", "CatrgoryItem") - - graphs_train.add_graph_node_property(graph_id, "User", "U_" + str(X_train[graph_id][0])) - graphs_train.add_graph_node_property(graph_id, "Item", "I_" + str(X_train[graph_id][1])) - graphs_train.add_graph_node_property(graph_id, "Category", "C_" + str(X_train[graph_id][2])) -graphs_train.encode() -print("Training data produced") - -# Test data -graphs_test = Graphs(X_test.shape[0], init_with=graphs_train) -for graph_id in range(X_test.shape[0]): - graphs_test.set_number_of_graph_nodes(graph_id, number_of_nodes) -graphs_test.prepare_node_configuration() -for graph_id in range(X_test.shape[0]): - for node_id in range(graphs_test.number_of_graph_nodes[graph_id]): - number_of_edges = 2 if node_id > 0 and node_id < graphs_test.number_of_graph_nodes[graph_id]-1 else 1 - if node_id == 0: - graphs_test.add_graph_node(graph_id, "User", number_of_edges) - elif node_id == 1: - graphs_test.add_graph_node(graph_id, "Item", number_of_edges) - else: - graphs_test.add_graph_node(graph_id, "Category", number_of_edges) -graphs_test.prepare_edge_configuration() -for graph_id in range(X_test.shape[0]): - for node_id in range(graphs_test.number_of_graph_nodes[graph_id]): - if node_id == 0: - graphs_test.add_graph_node_edge(graph_id, "User", "Item", "UserItem") - - if node_id == 1: - graphs_test.add_graph_node_edge(graph_id, "Item", "Category", "ItemCategory") - graphs_test.add_graph_node_edge(graph_id, "Item", "User", "ItemUser") - - if node_id == 2: - graphs_test.add_graph_node_edge(graph_id, "Category", "Item", "CatrgoryItem") - - graphs_test.add_graph_node_property(graph_id, "User", "U_" + str(X_test[graph_id][0])) - graphs_test.add_graph_node_property(graph_id, "Item", "I_" + str(X_test[graph_id][1])) - graphs_test.add_graph_node_property(graph_id, "Category", "C_" + str(X_test[graph_id][2])) -graphs_test.encode() -print("Testing data produced") - -tm = MultiClassGraphTsetlinMachine( - args.number_of_clauses, - args.T, - args.s, - number_of_state_bits = args.number_of_state_bits, - depth=args.depth, - message_size=args.message_size, - message_bits=args.message_bits, - max_included_literals=args.max_included_literals, - double_hashing = args.double_hashing -) - -for i in range(args.epochs): - start_training = time() - tm.fit(graphs_train, Y_train, epochs=1, incremental=True) - stop_training = time() - - start_testing = time() - result_test = 100*(tm.predict(graphs_test) == Y_test).mean() - stop_testing = time() - - result_train = 100*(tm.predict(graphs_train) == Y_train).mean() - print("%d %.2f %.2f %.2f %.2f" % (i, result_train, result_test, stop_training-start_training, stop_testing-start_testing)) -# weights = tm.get_state()[1].reshape(2, -1) -# for i in range(tm.number_of_clauses): -# print("Clause #%d W:(%d %d)" % (i, weights[0,i], weights[1,i]), end=' ') -# l = [] -# for k in range(args.hypervector_size * 2): -# if tm.ta_action(0, i, k): -# if k < args.hypervector_size: -# l.append("x%d" % (k)) -# else: -# l.append("NOT x%d" % (k - args.hypervector_size)) -# for k in range(args.message_size * 2): -# if tm.ta_action(1, i, k): -# if k < args.message_size: -# l.append("c%d" % (k)) -# else: -# l.append("NOT c%d" % (k - args.message_size)) -# print(" AND ".join(l)) -# print(graphs_test.hypervectors) -# print(tm.hypervectors) -# print(graphs_test.edge_type_id) \ No newline at end of file +if __name__ == "__main__": + main(default_args()) \ No newline at end of file diff --git a/examples/applications/products_recommendation/main.sh b/examples/applications/products_recommendation/main.sh new file mode 100644 index 00000000..8c7a22ad --- /dev/null +++ b/examples/applications/products_recommendation/main.sh @@ -0,0 +1,16 @@ +echo `date`, Setup the environment ... +set -e # exit if error + +models="graph_tm tm_classifier graph_nn" +dataset_noise_ratios="0.005 0.01 0.02 0.05 0.1 0.2" + +for N in $dataset_noise_ratios; do + echo `date`, Running Graph NN ... + python3 graph_nn.py --dataset_noise_ratio $N + + echo `date`, Running Graph Tsetlin Machine ... + python3 graph_tm.py --dataset_noise_ratio $N + + echo `date`, Running Tsetlin Machine Classifier ... + python3 tm_classifier.py --dataset_noise_ratio $N +done \ No newline at end of file diff --git a/examples/applications/products_recommendation/test.ipynb b/examples/applications/products_recommendation/test.ipynb deleted file mode 100644 index 1465bf14..00000000 --- a/examples/applications/products_recommendation/test.ipynb +++ /dev/null @@ -1,271 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/usr/local/lib/python3.10/dist-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", - " from .autonotebook import tqdm as notebook_tqdm\n", - "usage: ipykernel_launcher.py [-h] [--epochs EPOCHS]\n", - " [--number-of-clauses NUMBER_OF_CLAUSES] [--T T]\n", - " [--s S]\n", - " [--number-of-state-bits NUMBER_OF_STATE_BITS]\n", - " [--depth DEPTH]\n", - " [--hypervector-size HYPERVECTOR_SIZE]\n", - " [--hypervector-bits HYPERVECTOR_BITS]\n", - " [--message-size MESSAGE_SIZE]\n", - " [--message-bits MESSAGE_BITS] [--double-hashing]\n", - " [--noise NOISE]\n", - " [--max-included-literals MAX_INCLUDED_LITERALS]\n", - "ipykernel_launcher.py: error: unrecognized arguments: --f=/root/.local/share/jupyter/runtime/kernel-v306f6e67794e909fd94dbef768cafee2e613728cc.json\n" - ] - }, - { - "ename": "SystemExit", - "evalue": "2", - "output_type": "error", - "traceback": [ - "An exception has occurred, use %tb to see the full traceback.\n", - "\u001b[0;31mSystemExit\u001b[0m\u001b[0;31m:\u001b[0m 2\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/root/.local/lib/python3.10/site-packages/IPython/core/interactiveshell.py:3585: UserWarning: To exit: use 'exit', 'quit', or Ctrl-D.\n", - " warn(\"To exit: use 'exit', 'quit', or Ctrl-D.\", stacklevel=1)\n" - ] - } - ], - "source": [ - "from GraphTsetlinMachine.graphs import Graphs\n", - "from GraphTsetlinMachine.tm import MultiClassGraphTsetlinMachine\n", - "from time import time\n", - "import argparse\n", - "import pandas as pd\n", - "import numpy as np\n", - "import kagglehub\n", - "from sklearn.model_selection import train_test_split\n", - "from sklearn.preprocessing import LabelEncoder\n", - "\n", - "def default_args(**kwargs):\n", - " parser = argparse.ArgumentParser()\n", - " parser.add_argument(\"--epochs\", default=250, type=int)\n", - " parser.add_argument(\"--number-of-clauses\", default=10000, type=int)\n", - " parser.add_argument(\"--T\", default=10000, type=int)\n", - " parser.add_argument(\"--s\", default=10.0, type=float)\n", - " parser.add_argument(\"--number-of-state-bits\", default=8, type=int)\n", - " parser.add_argument(\"--depth\", default=1, type=int)\n", - " parser.add_argument(\"--hypervector-size\", default=4096, type=int)\n", - " parser.add_argument(\"--hypervector-bits\", default=256, type=int)\n", - " parser.add_argument(\"--message-size\", default=4096, type=int)\n", - " parser.add_argument(\"--message-bits\", default=256, type=int)\n", - " parser.add_argument('--double-hashing', dest='double_hashing', default=False, action='store_true')\n", - " parser.add_argument(\"--noise\", default=0.01, type=float)\n", - " parser.add_argument(\"--max-included-literals\", default=10, type=int)\n", - "\n", - " args = parser.parse_args()\n", - " for key, value in kwargs.items():\n", - " if key in args.__dict__:\n", - " setattr(args, key, value)\n", - " return args\n", - "\n", - "args = default_args()\n", - "\n", - "############################# real dataset ########################\n", - "\n", - "print(\"Creating training data\")\n", - "path = kagglehub.dataset_download(\"karkavelrajaj/amazon-sales-dataset\")\n", - "print(\"Path to dataset files:\", path)\n", - "data_file = path + \"/amazon.csv\" \n", - "org_data = pd.read_csv(data_file)\n", - "# print(\"Data preview:\", data.head())\n", - "org_data = org_data[['product_id', 'category', 'user_id', 'rating']]\n", - "#################################### expanded \n", - "org_data['rating'] = pd.to_numeric(org_data['rating'], errors='coerce') # Coerce invalid values to NaN\n", - "org_data.dropna(subset=['rating'], inplace=True) # Drop rows with NaN ratings\n", - "org_data['rating'] = org_data['rating'].astype(int)\n", - "# Expand the dataset 10 times\n", - "data = pd.concat([org_data] * 10, ignore_index=True)\n", - "\n", - "# Shuffle the expanded dataset\n", - "data = data.sample(frac=1, random_state=42).reset_index(drop=True)\n", - "\n", - "# Add noise\n", - "# Define the noise ratio\n", - "noise_ratio = 0.1 # 10% noise\n", - "\n", - "# Select rows to apply noise\n", - "num_noisy_rows = int(noise_ratio * len(data))\n", - "noisy_indices = np.random.choice(data.index, size=num_noisy_rows, replace=False)\n", - "\n", - "# Add noise to ratings\n", - "data.loc[noisy_indices, 'rating'] = np.random.choice(range(1, 6), size=num_noisy_rows)\n", - "\n", - "# Add noise to categories\n", - "unique_categories = data['category'].unique()\n", - "data.loc[noisy_indices, 'category'] = np.random.choice(unique_categories, size=num_noisy_rows)\n", - "\n", - "# Print a preview of the noisy and expanded dataset\n", - "print(\"Original data shape:\", org_data.shape)\n", - "print(\"Expanded data shape:\", data.shape)\n", - "print(\"Data preview:\\n\", data.head())\n", - "\n", - "print(data.head())\n", - " \n", - "le_user = LabelEncoder()\n", - "le_item = LabelEncoder()\n", - "le_category = LabelEncoder()\n", - "le_rating = LabelEncoder() \n", - "\n", - "data['user_id'] = le_user.fit_transform(data['user_id'])\n", - "data['product_id'] = le_item.fit_transform(data['product_id'])\n", - "data['category'] = le_category.fit_transform(data['category'])\n", - "data['rating'] = le_rating.fit_transform(data['rating'])\n", - "\n", - "x = data[['user_id', 'product_id', 'category']].values \n", - "y = data['rating'].values \n", - "\n", - "X_train, X_test, Y_train, Y_test = train_test_split(x, y, test_size=0.2, random_state=42)\n", - "\n", - "print(\"X_train shape:\", X_train.shape)\n", - "print(\"y_train shape:\", Y_train.shape)\n", - "print(\"X_test shape:\", X_test.shape)\n", - "print(\"y_test shape:\", Y_test.shape)\n", - "\n", - "users = data['user_id'].unique()\n", - "items = data['product_id'].unique()\n", - "categories = data['category'].unique()\n", - "\n", - "# Initialize Graphs with symbols for GTM\n", - "number_of_nodes = 3\n", - "symbols = []\n", - "symbols = [\"U_\" + str(u) for u in users] + [\"I_\" + str(i) for i in items] + [\"C_\" + str(c) for c in categories] \n", - "print(len(symbols))\n", - "# Train data\n", - "graphs_train = Graphs(\n", - " X_train.shape[0],\n", - " symbols=symbols,\n", - " hypervector_size=args.hypervector_size,\n", - " hypervector_bits=args.hypervector_bits,\n", - " double_hashing = args.double_hashing\n", - ")\n", - "for graph_id in range(X_train.shape[0]):\n", - " graphs_train.set_number_of_graph_nodes(graph_id, number_of_nodes)\n", - "graphs_train.prepare_node_configuration()\n", - "for graph_id in range(X_train.shape[0]):\n", - " for node_id in range(graphs_train.number_of_graph_nodes[graph_id]):\n", - " number_of_edges = 2 if node_id > 0 and node_id < graphs_train.number_of_graph_nodes[graph_id]-1 else 1\n", - " if node_id == 0:\n", - " graphs_train.add_graph_node(graph_id, \"User\", number_of_edges)\n", - " elif node_id == 1:\n", - " graphs_train.add_graph_node(graph_id, \"Item\", number_of_edges)\n", - " else:\n", - " graphs_train.add_graph_node(graph_id, \"Category\", number_of_edges)\n", - "graphs_train.prepare_edge_configuration()\n", - "for graph_id in range(X_train.shape[0]):\n", - " for node_id in range(graphs_train.number_of_graph_nodes[graph_id]):\n", - " if node_id == 0:\n", - " graphs_train.add_graph_node_edge(graph_id, \"User\", \"Item\", \"UserItem\")\n", - " \n", - " if node_id == 1:\n", - " graphs_train.add_graph_node_edge(graph_id, \"Item\", \"Category\", \"ItemCategory\")\n", - " graphs_train.add_graph_node_edge(graph_id, \"Item\", \"User\", \"ItemUser\")\n", - " \n", - " if node_id == 2:\n", - " graphs_train.add_graph_node_edge(graph_id, \"Category\", \"Item\", \"CatrgoryItem\")\n", - "\n", - " graphs_train.add_graph_node_property(graph_id, \"User\", \"U_\" + str(X_train[graph_id][0]))\n", - " graphs_train.add_graph_node_property(graph_id, \"Item\", \"I_\" + str(X_train[graph_id][1]))\n", - " graphs_train.add_graph_node_property(graph_id, \"Category\", \"C_\" + str(X_train[graph_id][2]))\n", - "graphs_train.encode()\n", - "print(\"Training data produced\")\n", - "\n", - "# Test data\n", - "graphs_test = Graphs(X_test.shape[0], init_with=graphs_train)\n", - "for graph_id in range(X_test.shape[0]):\n", - " graphs_test.set_number_of_graph_nodes(graph_id, number_of_nodes)\n", - "graphs_test.prepare_node_configuration()\n", - "for graph_id in range(X_test.shape[0]):\n", - " for node_id in range(graphs_test.number_of_graph_nodes[graph_id]):\n", - " number_of_edges = 2 if node_id > 0 and node_id < graphs_test.number_of_graph_nodes[graph_id]-1 else 1\n", - " if node_id == 0:\n", - " graphs_test.add_graph_node(graph_id, \"User\", number_of_edges)\n", - " elif node_id == 1:\n", - " graphs_test.add_graph_node(graph_id, \"Item\", number_of_edges)\n", - " else:\n", - " graphs_test.add_graph_node(graph_id, \"Category\", number_of_edges)\n", - "graphs_test.prepare_edge_configuration()\n", - "for graph_id in range(X_test.shape[0]):\n", - " for node_id in range(graphs_test.number_of_graph_nodes[graph_id]):\n", - " if node_id == 0:\n", - " graphs_test.add_graph_node_edge(graph_id, \"User\", \"Item\", \"UserItem\")\n", - " \n", - " if node_id == 1:\n", - " graphs_test.add_graph_node_edge(graph_id, \"Item\", \"Category\", \"ItemCategory\")\n", - " graphs_test.add_graph_node_edge(graph_id, \"Item\", \"User\", \"ItemUser\")\n", - " \n", - " if node_id == 2:\n", - " graphs_test.add_graph_node_edge(graph_id, \"Category\", \"Item\", \"CatrgoryItem\")\n", - "\n", - " graphs_test.add_graph_node_property(graph_id, \"User\", \"U_\" + str(X_test[graph_id][0]))\n", - " graphs_test.add_graph_node_property(graph_id, \"Item\", \"I_\" + str(X_test[graph_id][1]))\n", - " graphs_test.add_graph_node_property(graph_id, \"Category\", \"C_\" + str(X_test[graph_id][2]))\n", - "graphs_test.encode()\n", - "print(\"Testing data produced\")\n", - "\n", - "tm = MultiClassGraphTsetlinMachine(\n", - " args.number_of_clauses,\n", - " args.T,\n", - " args.s,\n", - " number_of_state_bits = args.number_of_state_bits,\n", - " depth=args.depth,\n", - " message_size=args.message_size,\n", - " message_bits=args.message_bits,\n", - " max_included_literals=args.max_included_literals,\n", - " double_hashing = args.double_hashing\n", - ")\n", - "\n", - "for i in range(args.epochs):\n", - " start_training = time()\n", - " tm.fit(graphs_train, Y_train, epochs=1, incremental=True)\n", - " stop_training = time()\n", - "\n", - " start_testing = time()\n", - " result_test = 100*(tm.predict(graphs_test) == Y_test).mean()\n", - " stop_testing = time()\n", - "\n", - " result_train = 100*(tm.predict(graphs_train) == Y_train).mean()\n", - "\n", - " print(\"%d %.2f %.2f %.2f %.2f\" % (i, result_train, result_test, stop_training-start_training, stop_testing-start_testing))" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.12" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/examples/applications/products_recommendation/tm_classifier.py b/examples/applications/products_recommendation/tm_classifier.py index b390764a..1a2928d0 100644 --- a/examples/applications/products_recommendation/tm_classifier.py +++ b/examples/applications/products_recommendation/tm_classifier.py @@ -1,60 +1,59 @@ -import logging import argparse from tmu.models.classification.vanilla_classifier import TMClassifier from tmu.tools import BenchmarkTimer -from tmu.util.cuda_profiler import CudaProfiler import prepare_dataset +import pandas as pd +import os -_LOGGER = logging.getLogger(__name__) - -def metrics(args): - return dict( - accuracy=[], - train_time=[], - test_time=[], - args=vars(args) - ) - -def main(args): - experiment_results = metrics(args) - data = prepare_dataset.aug_amazon_products() +def main(args): + results = [] + data = prepare_dataset.aug_amazon_products(noise_ratio = args.dataset_noise_ratio) x, y = prepare_dataset.construct_x_y(data) X_train, X_test, Y_train, Y_test = prepare_dataset.one_hot_encoding(x,y) - tm = TMClassifier( number_of_clauses=args.num_clauses, T=args.T, s=args.s, max_included_literals=args.max_included_literals, platform=args.platform, - weighted_clauses=args.weighted_clauses + weighted_clauses=args.weighted_clauses, ) - _LOGGER.info(f"Running {TMClassifier} for {args.epochs}") + for epoch in range(args.epochs): benchmark_total = BenchmarkTimer(logger=None, text="Epoch Time") with benchmark_total: benchmark1 = BenchmarkTimer(logger=None, text="Training Time") with benchmark1: - res = tm.fit( - X_train, - Y_train, - ) - - experiment_results["train_time"].append(benchmark1.elapsed()) + tm.fit(X_train, Y_train) + train_time = benchmark1.elapsed() benchmark2 = BenchmarkTimer(logger=None, text="Testing Time") with benchmark2: - result = 100 * (tm.predict(X_test) == Y_test).mean() - experiment_results["accuracy"].append(result) - experiment_results["test_time"].append(benchmark2.elapsed()) - - _LOGGER.info(f"Epoch: {epoch + 1}, Accuracy: {result:.2f}, Training Time: {benchmark1.elapsed():.2f}s, " - f"Testing Time: {benchmark2.elapsed():.2f}s") - - if args.platform == "CUDA": - CudaProfiler().print_timings(benchmark=benchmark_total) - - return experiment_results + accuracy = 100 * (tm.predict(X_test) == Y_test).mean() + test_time = benchmark2.elapsed() + total_time = benchmark_total.elapsed() + + # Append results for each epoch + results.append({ + "Algorithm": "TMClassifier", + "Noise_Ratio": args.dataset_noise_ratio, + "T": args.T, + "s": args.s, + "Max_Included_Literals": args.max_included_literals, + "Epochs": args.epochs, + "Platform": args.platform, + "Total_Time": total_time, + "Accuracy": accuracy, + }) + # Save results to CSV + results_df = pd.DataFrame(results) + results_file = "experiment_results.csv" + if os.path.exists(results_file): + results_df.to_csv(results_file, mode='a', index=False, header=False) + else: + results_df.to_csv(results_file, index=False) + print(f"Results saved to {results_file}") + def default_args(**kwargs): parser = argparse.ArgumentParser() parser.add_argument("--num_clauses", default=2000, type=int) @@ -64,6 +63,7 @@ def default_args(**kwargs): parser.add_argument("--platform", default="CPU_sparse", type=str, choices=["CPU", "CPU_sparse", "CUDA"]) parser.add_argument("--weighted_clauses", default=True, type=bool) parser.add_argument("--epochs", default=10, type=int) + parser.add_argument("--dataset_noise_ratio", default=0.01, type=float) args = parser.parse_args() for key, value in kwargs.items(): if key in args.__dict__: @@ -71,5 +71,4 @@ def default_args(**kwargs): return args if __name__ == "__main__": - results = main(default_args()) - _LOGGER.info(results) \ No newline at end of file + main(default_args()) \ No newline at end of file From 84d8012259f3a253f102b6321f508a1da474743d Mon Sep 17 00:00:00 2001 From: Ahmed Khalid Date: Wed, 25 Dec 2024 15:40:00 +0000 Subject: [PATCH 21/33] add results --- .../experiment_results.csv | 6 ------ examples/recomm_system/README.md | 2 ++ .../prepare_dataset.cpython-310.pyc | Bin examples/recomm_system/experiment_results.csv | 19 ++++++++++++++++++ .../graph_nn.py | 0 .../graph_tm.py | 0 .../main.sh | 0 .../prepare_dataset.py | 0 .../tm_classifier.py | 0 9 files changed, 21 insertions(+), 6 deletions(-) delete mode 100644 examples/applications/products_recommendation/experiment_results.csv create mode 100644 examples/recomm_system/README.md rename examples/{applications/products_recommendation => recomm_system}/__pycache__/prepare_dataset.cpython-310.pyc (100%) create mode 100644 examples/recomm_system/experiment_results.csv rename examples/{applications/products_recommendation => recomm_system}/graph_nn.py (100%) rename examples/{applications/products_recommendation => recomm_system}/graph_tm.py (100%) rename examples/{applications/products_recommendation => recomm_system}/main.sh (100%) rename examples/{applications/products_recommendation => recomm_system}/prepare_dataset.py (100%) rename examples/{applications/products_recommendation => recomm_system}/tm_classifier.py (100%) diff --git a/examples/applications/products_recommendation/experiment_results.csv b/examples/applications/products_recommendation/experiment_results.csv deleted file mode 100644 index d3f66d27..00000000 --- a/examples/applications/products_recommendation/experiment_results.csv +++ /dev/null @@ -1,6 +0,0 @@ -Algorithm,Noise_Ratio,T,s,Max_Included_Literals,Epochs,Platform,Total_Time,Accuracy -Graph NN,0.005,0,0,0,1000,CPU,0.03006434440612793,76.72131061553955 -GraphTM,0.005,10000,10.0,23,10,CUDA,34.547648191452026,98.46994535519126 -TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,89.6943154335022,76.63934426229508 -Graph NN,0.01,0,0,0,1000,CPU,0.01817464828491211,75.95628499984741 -GraphTM,0.01,10000,10.0,23,10,CUDA,34.95576763153076,98.44262295081967 diff --git a/examples/recomm_system/README.md b/examples/recomm_system/README.md new file mode 100644 index 00000000..e7fa211a --- /dev/null +++ b/examples/recomm_system/README.md @@ -0,0 +1,2 @@ +cd examples/recomm_system/ +bash main.sh \ No newline at end of file diff --git a/examples/applications/products_recommendation/__pycache__/prepare_dataset.cpython-310.pyc b/examples/recomm_system/__pycache__/prepare_dataset.cpython-310.pyc similarity index 100% rename from examples/applications/products_recommendation/__pycache__/prepare_dataset.cpython-310.pyc rename to examples/recomm_system/__pycache__/prepare_dataset.cpython-310.pyc diff --git a/examples/recomm_system/experiment_results.csv b/examples/recomm_system/experiment_results.csv new file mode 100644 index 00000000..cb6e80f7 --- /dev/null +++ b/examples/recomm_system/experiment_results.csv @@ -0,0 +1,19 @@ +Algorithm,Noise_Ratio,T,s,Max_Included_Literals,Epochs,Platform,Total_Time,Accuracy +Graph NN,0.005,0,0,0,1000,CPU,0.03006434440612793,76.72131061553955 +GraphTM,0.005,10000,10.0,23,10,CUDA,34.547648191452026,98.46994535519126 +TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,89.6943154335022,76.63934426229508 +Graph NN,0.01,0,0,0,1000,CPU,0.01817464828491211,75.95628499984741 +GraphTM,0.01,10000,10.0,23,10,CUDA,34.95576763153076,98.44262295081967 +TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,96.10501098632812,74.93169398907104 +Graph NN,0.02,0,0,0,1000,CPU,0.03073263168334961,81.22950792312622 +GraphTM,0.02,10000,10.0,23,10,CUDA,36.0724892616272,97.43169398907104 +TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,95.67133641242981,72.40437158469946 +Graph NN,0.05,0,0,0,1000,CPU,0.014258623123168945,83.52459073066711 +GraphTM,0.05,10000,10.0,23,10,CUDA,38.86628317832947,95.0 +TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,96.7427487373352,64.65163934426229 +Graph NN,0.1,0,0,0,1000,CPU,0.022305965423583984,73.33333492279053 +GraphTM,0.1,10000,10.0,23,10,CUDA,37.45086216926575,90.08196721311475 +TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,90.45554423332214,49.8292349726776 +Graph NN,0.2,0,0,0,1000,CPU,0.03204679489135742,59.863388538360596 +GraphTM,0.2,10000,10.0,23,10,CUDA,16.268279790878296,78.77049180327869 +TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,96.16712856292725,20.184426229508194 diff --git a/examples/applications/products_recommendation/graph_nn.py b/examples/recomm_system/graph_nn.py similarity index 100% rename from examples/applications/products_recommendation/graph_nn.py rename to examples/recomm_system/graph_nn.py diff --git a/examples/applications/products_recommendation/graph_tm.py b/examples/recomm_system/graph_tm.py similarity index 100% rename from examples/applications/products_recommendation/graph_tm.py rename to examples/recomm_system/graph_tm.py diff --git a/examples/applications/products_recommendation/main.sh b/examples/recomm_system/main.sh similarity index 100% rename from examples/applications/products_recommendation/main.sh rename to examples/recomm_system/main.sh diff --git a/examples/applications/products_recommendation/prepare_dataset.py b/examples/recomm_system/prepare_dataset.py similarity index 100% rename from examples/applications/products_recommendation/prepare_dataset.py rename to examples/recomm_system/prepare_dataset.py diff --git a/examples/applications/products_recommendation/tm_classifier.py b/examples/recomm_system/tm_classifier.py similarity index 100% rename from examples/applications/products_recommendation/tm_classifier.py rename to examples/recomm_system/tm_classifier.py From d68ae7153845b1ed2f09ebf2b2726a9e21444b99 Mon Sep 17 00:00:00 2001 From: Ahmed Khalid Date: Thu, 26 Dec 2024 16:01:53 +0000 Subject: [PATCH 22/33] fair comparisons --- examples/recomm_system/experiment_results.csv | 36 +++++++++++++++++++ examples/recomm_system/graph_nn.py | 12 +++---- examples/recomm_system/graph_tm.py | 10 +++--- examples/recomm_system/tm_classifier.py | 10 +++--- 4 files changed, 52 insertions(+), 16 deletions(-) diff --git a/examples/recomm_system/experiment_results.csv b/examples/recomm_system/experiment_results.csv index cb6e80f7..957f7701 100644 --- a/examples/recomm_system/experiment_results.csv +++ b/examples/recomm_system/experiment_results.csv @@ -17,3 +17,39 @@ TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,90.45554423332214,49.8292349726776 Graph NN,0.2,0,0,0,1000,CPU,0.03204679489135742,59.863388538360596 GraphTM,0.2,10000,10.0,23,10,CUDA,16.268279790878296,78.77049180327869 TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,96.16712856292725,20.184426229508194 +Graph NN,0.005,0,0,0,1000,CPU,0.0168764591217041,76.85792446136475 +GraphTM,0.005,10000,10.0,23,10,CUDA,31.40691065788269,98.82513661202185 +TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,88.05298614501953,76.74180327868852 +Graph NN,0.01,0,0,0,1000,CPU,0.01720118522644043,87.4316930770874 +GraphTM,0.01,10000,10.0,23,10,CUDA,31.529547214508057,98.4153005464481 +TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,89.19472336769104,74.93169398907104 +Graph NN,0.02,0,0,0,1000,CPU,0.014032602310180664,78.36065292358398 +GraphTM,0.02,10000,10.0,23,10,CUDA,32.8007595539093,97.62295081967213 +TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,94.56675243377686,72.6775956284153 +Graph NN,0.05,0,0,0,1000,CPU,0.016784191131591797,76.88524723052979 +GraphTM,0.05,10000,10.0,23,10,CUDA,34.84256434440613,94.75409836065573 +TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,96.4975814819336,64.1051912568306 +Graph NN,0.1,0,0,0,1000,CPU,0.014883041381835938,70.54644823074341 +GraphTM,0.1,10000,10.0,23,10,CUDA,36.750433683395386,89.97267759562841 +TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,96.35110449790955,50.341530054644814 +Graph NN,0.2,0,0,0,1000,CPU,0.03427433967590332,61.50273084640503 +GraphTM,0.2,10000,10.0,23,10,CUDA,39.63756251335144,79.01639344262294 +TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,97.00698733329773,20.116120218579233 +Graph NN,0.005,0,0,0,20000,CPU,370.7295939922333,87.5683069229126 +GraphTM,0.005,10000,10.0,23,10,CUDA,342.7878243923187,98.82513661202185 +TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,954.4101324081421,76.63934426229508 +Graph NN,0.01,0,0,0,20000,CPU,304.6031119823456,86.74863576889038 +GraphTM,0.01,10000,10.0,23,10,CUDA,346.8704605102539,98.25136612021858 +TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,978.3629264831543,74.93169398907104 +Graph NN,0.02,0,0,0,20000,CPU,403.2585175037384,75.30054450035095 +GraphTM,0.02,10000,10.0,23,10,CUDA,353.39254236221313,97.65027322404372 +TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,971.3300836086273,72.1311475409836 +Graph NN,0.05,0,0,0,20000,CPU,398.8085067272186,93.8524603843689 +GraphTM,0.05,10000,10.0,23,10,CUDA,368.16111874580383,94.59016393442623 +TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,960.4506890773773,63.661202185792355 +Graph NN,0.1,0,0,0,20000,CPU,388.4886665344238,75.43715834617615 +GraphTM,0.1,10000,10.0,23,10,CUDA,340.63327074050903,90.43715846994536 +TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,972.1077370643616,49.35109289617486 +Graph NN,0.2,0,0,0,20000,CPU,438.5506749153137,64.04371857643127 +GraphTM,0.2,10000,10.0,23,10,CUDA,357.2651107311249,77.89617486338798 +TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,948.7157049179077,20.116120218579233 diff --git a/examples/recomm_system/graph_nn.py b/examples/recomm_system/graph_nn.py index 30292db9..9ef5fbed 100644 --- a/examples/recomm_system/graph_nn.py +++ b/examples/recomm_system/graph_nn.py @@ -61,9 +61,9 @@ def forward(self, x, edge_index): ).t() test_labels = torch.tensor(Y_test, dtype=torch.float) # Training Loop with Accuracy Logging - for epoch in range(args.epochs): - benchmark_total = BenchmarkTimer(logger=None, text="Epoch Time") - with benchmark_total: + benchmark_total = BenchmarkTimer(logger=None, text="Epochs Time") + with benchmark_total: + for epoch in range(args.epochs): benchmark1 = BenchmarkTimer(logger=None, text="Training Time") with benchmark1: # Training Phase @@ -91,8 +91,8 @@ def forward(self, x, edge_index): # Compute accuracy accuracy = ((test_predicted_ratings.round() == test_labels).float().mean().item()) * 100 test_time = benchmark2.elapsed() - total_time = benchmark_total.elapsed() - # Append results for each epoch + total_time = benchmark_total.elapsed() + # Append results for each epoch results.append({ "Algorithm": "Graph NN", "Noise_Ratio": args.dataset_noise_ratio, @@ -118,7 +118,7 @@ def forward(self, x, edge_index): def default_args(**kwargs): parser = argparse.ArgumentParser() parser.add_argument("--platform", default="CPU", type=str, choices=["CPU", "CUDA"]) - parser.add_argument("--epochs", default=1000, type=int) + parser.add_argument("--epochs", default=20000, type=int) parser.add_argument("--dataset_noise_ratio", default=0.01, type=float) args = parser.parse_args() for key, value in kwargs.items(): diff --git a/examples/recomm_system/graph_tm.py b/examples/recomm_system/graph_tm.py index 0ec2171c..d1464c75 100644 --- a/examples/recomm_system/graph_tm.py +++ b/examples/recomm_system/graph_tm.py @@ -106,9 +106,9 @@ def main(args): double_hashing = args.double_hashing ) - for epoch in range(args.epochs): - benchmark_total = BenchmarkTimer(logger=None, text="Epoch Time") - with benchmark_total: + benchmark_total = BenchmarkTimer(logger=None, text="Epoch Time") + with benchmark_total: + for epoch in range(args.epochs): benchmark1 = BenchmarkTimer(logger=None, text="Training Time") with benchmark1: tm.fit(graphs_train, Y_train, epochs=1, incremental=True) @@ -118,8 +118,8 @@ def main(args): with benchmark2: accuracy = 100*(tm.predict(graphs_test) == Y_test).mean() test_time = benchmark2.elapsed() - total_time = benchmark_total.elapsed() - # result_train = 100*(tm.predict(graphs_train) == Y_train).mean() + total_time = benchmark_total.elapsed() + # result_train = 100*(tm.predict(graphs_train) == Y_train).mean() results.append({ "Algorithm": "GraphTM", "Noise_Ratio": args.dataset_noise_ratio, diff --git a/examples/recomm_system/tm_classifier.py b/examples/recomm_system/tm_classifier.py index 1a2928d0..876f8c4f 100644 --- a/examples/recomm_system/tm_classifier.py +++ b/examples/recomm_system/tm_classifier.py @@ -19,9 +19,9 @@ def main(args): weighted_clauses=args.weighted_clauses, ) - for epoch in range(args.epochs): - benchmark_total = BenchmarkTimer(logger=None, text="Epoch Time") - with benchmark_total: + benchmark_total = BenchmarkTimer(logger=None, text="Epoch Time") + with benchmark_total: + for epoch in range(args.epochs): benchmark1 = BenchmarkTimer(logger=None, text="Training Time") with benchmark1: tm.fit(X_train, Y_train) @@ -30,9 +30,9 @@ def main(args): with benchmark2: accuracy = 100 * (tm.predict(X_test) == Y_test).mean() test_time = benchmark2.elapsed() - total_time = benchmark_total.elapsed() + total_time = benchmark_total.elapsed() - # Append results for each epoch + # Append results for each epoch results.append({ "Algorithm": "TMClassifier", "Noise_Ratio": args.dataset_noise_ratio, From c3d895b2840f9cb98fd19512ba7df5682d231647 Mon Sep 17 00:00:00 2001 From: Ahmed Khalid Date: Mon, 17 Feb 2025 11:34:50 +0000 Subject: [PATCH 23/33] update --- examples/recomm_system/experiment_results.csv | 234 +++++++++++---- .../recomm_system/experiment_results_old.csv | 271 ++++++++++++++++++ examples/recomm_system/graph_tm.py | 6 + examples/recomm_system/main.sh | 28 +- examples/recomm_system/test.ipynb | 186 ++++++++++++ 5 files changed, 662 insertions(+), 63 deletions(-) create mode 100644 examples/recomm_system/experiment_results_old.csv create mode 100644 examples/recomm_system/test.ipynb diff --git a/examples/recomm_system/experiment_results.csv b/examples/recomm_system/experiment_results.csv index 957f7701..b394dad6 100644 --- a/examples/recomm_system/experiment_results.csv +++ b/examples/recomm_system/experiment_results.csv @@ -1,55 +1,181 @@ Algorithm,Noise_Ratio,T,s,Max_Included_Literals,Epochs,Platform,Total_Time,Accuracy -Graph NN,0.005,0,0,0,1000,CPU,0.03006434440612793,76.72131061553955 -GraphTM,0.005,10000,10.0,23,10,CUDA,34.547648191452026,98.46994535519126 -TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,89.6943154335022,76.63934426229508 -Graph NN,0.01,0,0,0,1000,CPU,0.01817464828491211,75.95628499984741 -GraphTM,0.01,10000,10.0,23,10,CUDA,34.95576763153076,98.44262295081967 -TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,96.10501098632812,74.93169398907104 -Graph NN,0.02,0,0,0,1000,CPU,0.03073263168334961,81.22950792312622 -GraphTM,0.02,10000,10.0,23,10,CUDA,36.0724892616272,97.43169398907104 -TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,95.67133641242981,72.40437158469946 -Graph NN,0.05,0,0,0,1000,CPU,0.014258623123168945,83.52459073066711 -GraphTM,0.05,10000,10.0,23,10,CUDA,38.86628317832947,95.0 -TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,96.7427487373352,64.65163934426229 -Graph NN,0.1,0,0,0,1000,CPU,0.022305965423583984,73.33333492279053 -GraphTM,0.1,10000,10.0,23,10,CUDA,37.45086216926575,90.08196721311475 -TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,90.45554423332214,49.8292349726776 -Graph NN,0.2,0,0,0,1000,CPU,0.03204679489135742,59.863388538360596 -GraphTM,0.2,10000,10.0,23,10,CUDA,16.268279790878296,78.77049180327869 -TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,96.16712856292725,20.184426229508194 -Graph NN,0.005,0,0,0,1000,CPU,0.0168764591217041,76.85792446136475 -GraphTM,0.005,10000,10.0,23,10,CUDA,31.40691065788269,98.82513661202185 -TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,88.05298614501953,76.74180327868852 -Graph NN,0.01,0,0,0,1000,CPU,0.01720118522644043,87.4316930770874 -GraphTM,0.01,10000,10.0,23,10,CUDA,31.529547214508057,98.4153005464481 -TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,89.19472336769104,74.93169398907104 -Graph NN,0.02,0,0,0,1000,CPU,0.014032602310180664,78.36065292358398 -GraphTM,0.02,10000,10.0,23,10,CUDA,32.8007595539093,97.62295081967213 -TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,94.56675243377686,72.6775956284153 -Graph NN,0.05,0,0,0,1000,CPU,0.016784191131591797,76.88524723052979 -GraphTM,0.05,10000,10.0,23,10,CUDA,34.84256434440613,94.75409836065573 -TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,96.4975814819336,64.1051912568306 -Graph NN,0.1,0,0,0,1000,CPU,0.014883041381835938,70.54644823074341 -GraphTM,0.1,10000,10.0,23,10,CUDA,36.750433683395386,89.97267759562841 -TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,96.35110449790955,50.341530054644814 -Graph NN,0.2,0,0,0,1000,CPU,0.03427433967590332,61.50273084640503 -GraphTM,0.2,10000,10.0,23,10,CUDA,39.63756251335144,79.01639344262294 -TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,97.00698733329773,20.116120218579233 -Graph NN,0.005,0,0,0,20000,CPU,370.7295939922333,87.5683069229126 -GraphTM,0.005,10000,10.0,23,10,CUDA,342.7878243923187,98.82513661202185 -TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,954.4101324081421,76.63934426229508 -Graph NN,0.01,0,0,0,20000,CPU,304.6031119823456,86.74863576889038 -GraphTM,0.01,10000,10.0,23,10,CUDA,346.8704605102539,98.25136612021858 -TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,978.3629264831543,74.93169398907104 -Graph NN,0.02,0,0,0,20000,CPU,403.2585175037384,75.30054450035095 -GraphTM,0.02,10000,10.0,23,10,CUDA,353.39254236221313,97.65027322404372 -TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,971.3300836086273,72.1311475409836 -Graph NN,0.05,0,0,0,20000,CPU,398.8085067272186,93.8524603843689 -GraphTM,0.05,10000,10.0,23,10,CUDA,368.16111874580383,94.59016393442623 -TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,960.4506890773773,63.661202185792355 -Graph NN,0.1,0,0,0,20000,CPU,388.4886665344238,75.43715834617615 -GraphTM,0.1,10000,10.0,23,10,CUDA,340.63327074050903,90.43715846994536 -TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,972.1077370643616,49.35109289617486 -Graph NN,0.2,0,0,0,20000,CPU,438.5506749153137,64.04371857643127 -GraphTM,0.2,10000,10.0,23,10,CUDA,357.2651107311249,77.89617486338798 -TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,948.7157049179077,20.116120218579233 +Graph NN,0.005,0,0,0,20000,CPU,418.9250466823578,75.62841773033142 +GraphTM,0.005,10000,10.0,23,10,CUDA,110.35683226585388,98.68852459016394 +TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,1059.1634759902954,76.81010928961749 +Graph NN,0.01,0,0,0,20000,CPU,550.6980571746826,94.50819492340088 +GraphTM,0.01,10000,10.0,23,10,CUDA,114.06276345252991,98.4153005464481 +TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,1051.916612625122,75.3415300546448 +Graph NN,0.02,0,0,0,20000,CPU,475.44024682044983,75.30054450035095 +GraphTM,0.02,10000,10.0,23,10,CUDA,121.55624794960022,97.8415300546448 +TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,1043.9487817287445,72.1311475409836 +Graph NN,0.05,0,0,0,20000,CPU,411.8552327156067,80.98360896110535 +GraphTM,0.05,10000,10.0,23,10,CUDA,136.7814338207245,94.20765027322405 +TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,1044.2656917572021,64.00273224043715 +Graph NN,0.1,0,0,0,20000,CPU,484.6550889015198,68.7158465385437 +GraphTM,0.1,10000,10.0,23,10,CUDA,150.34457921981812,89.72677595628416 +TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,1061.191523551941,49.62431693989071 +Graph NN,0.2,0,0,0,20000,CPU,483.8463816642761,71.28415107727051 +GraphTM,0.2,10000,10.0,23,10,CUDA,169.18810439109802,78.49726775956285 +TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,1071.927158355713,20.116120218579233 +Graph NN,0.005,0,0,0,20000,CPU,473.5806052684784,86.36612296104431 +GraphTM,0.005,10000,10.0,23,10,CUDA,110.18979954719543,98.60655737704917 +TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,979.0509588718414,76.74180327868852 +Graph NN,0.01,0,0,0,20000,CPU,444.6897065639496,93.55190992355347 +GraphTM,0.01,10000,10.0,23,10,CUDA,112.48035550117493,98.4153005464481 +TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,1007.9654748439789,74.86338797814209 +Graph NN,0.02,0,0,0,20000,CPU,386.32835030555725,93.22404265403748 +GraphTM,0.02,10000,10.0,23,10,CUDA,120.46316766738892,97.73224043715847 +TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,1017.5866801738739,73.25819672131148 +Graph NN,0.05,0,0,0,20000,CPU,417.78410935401917,73.1693983078003 +GraphTM,0.05,10000,10.0,23,10,CUDA,135.64952206611633,95.08196721311475 +TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,945.0465729236603,64.00273224043715 +Graph NN,0.1,0,0,0,20000,CPU,481.6537721157074,77.18579173088074 +GraphTM,0.1,10000,10.0,23,10,CUDA,149.57958960533142,90.08196721311475 +TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,938.0212676525116,49.59016393442623 +Graph NN,0.2,0,0,0,20000,CPU,391.36059975624084,64.15300369262695 +GraphTM,0.2,10000,10.0,23,10,CUDA,169.49347591400146,77.65027322404372 +TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,940.9758951663971,20.116120218579233 +Graph NN,0.005,0,0,0,20000,CPU,480.5005066394806,75.68305730819702 +GraphTM,0.005,10000,10.0,23,10,CUDA,109.65927052497864,98.19672131147541 +TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,947.7581994533539,76.63934426229508 +Graph NN,0.01,0,0,0,20000,CPU,449.22584795951843,76.36612057685852 +GraphTM,0.01,10000,10.0,23,10,CUDA,113.07226181030273,98.27868852459017 +TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,1010.8711988925934,74.93169398907104 +Graph NN,0.02,0,0,0,20000,CPU,403.96647000312805,96.85792326927185 +GraphTM,0.02,10000,10.0,23,10,CUDA,120.02044725418091,97.78688524590164 +TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,1011.7896072864532,72.40437158469946 +Graph NN,0.05,0,0,0,20000,CPU,460.688773393631,85.00000238418579 +GraphTM,0.05,10000,10.0,23,10,CUDA,136.04891228675842,94.69945355191257 +TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,1014.1492829322815,64.00273224043715 +Graph NN,0.1,0,0,0,20000,CPU,407.9346880912781,74.1256833076477 +GraphTM,0.1,10000,10.0,23,10,CUDA,149.6586093902588,90.08196721311475 +TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,990.8282098770142,49.59016393442623 +Graph NN,0.2,0,0,0,20000,CPU,437.8108870983124,65.60109257698059 +GraphTM,0.2,10000,10.0,23,10,CUDA,168.44772601127625,78.93442622950819 +TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,1022.1848647594452,20.116120218579233 +Graph NN,0.005,0,0,0,20000,CPU,430.3925087451935,89.20764923095703 +GraphTM,0.005,10000,10.0,23,10,CUDA,109.6658935546875,98.68852459016394 +TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,1016.199923992157,76.63934426229508 +Graph NN,0.01,0,0,0,20000,CPU,396.3338620662689,84.23497080802917 +GraphTM,0.01,10000,10.0,23,10,CUDA,113.67849016189575,98.27868852459017 +TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,944.4602844715118,74.86338797814209 +Graph NN,0.02,0,0,0,20000,CPU,434.91951632499695,93.25136542320251 +GraphTM,0.02,10000,10.0,23,10,CUDA,120.31921482086182,97.8415300546448 +TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,933.2245874404907,72.40437158469946 +Graph NN,0.05,0,0,0,20000,CPU,483.2671537399292,80.32786846160889 +GraphTM,0.05,10000,10.0,23,10,CUDA,135.68922591209412,94.78142076502732 +TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,994.6384744644165,64.13934426229508 +Graph NN,0.1,0,0,0,20000,CPU,424.9935986995697,81.33879899978638 +GraphTM,0.1,10000,10.0,23,10,CUDA,149.08107113838196,89.59016393442623 +TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,944.0273253917694,49.62431693989071 +Graph NN,0.2,0,0,0,20000,CPU,333.49274706840515,61.50273084640503 +GraphTM,0.2,10000,10.0,23,10,CUDA,170.906751871109,78.98907103825137 +TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,965.9725024700165,20.116120218579233 +Graph NN,0.005,0,0,0,20000,CPU,377.28471970558167,75.68305730819702 +GraphTM,0.005,10000,10.0,23,10,CUDA,109.61631536483765,98.82513661202185 +TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,976.8008840084076,76.67349726775956 +Graph NN,0.01,0,0,0,20000,CPU,473.2922372817993,76.06557607650757 +GraphTM,0.01,10000,10.0,23,10,CUDA,112.87212014198303,98.27868852459017 +TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,942.7254059314728,74.86338797814209 +Graph NN,0.02,0,0,0,20000,CPU,357.36573815345764,75.40983557701111 +GraphTM,0.02,10000,10.0,23,10,CUDA,119.41612005233765,97.8415300546448 +TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,985.81947016716,72.40437158469946 +Graph NN,0.05,0,0,0,20000,CPU,440.75843334198,73.08743000030518 +GraphTM,0.05,10000,10.0,23,10,CUDA,136.8215868473053,94.91803278688525 +TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,997.739678144455,64.1051912568306 +Graph NN,0.1,0,0,0,20000,CPU,426.73446226119995,88.55191469192505 +GraphTM,0.1,10000,10.0,23,10,CUDA,149.54467248916626,89.94535519125682 +TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,980.096907377243,49.59016393442623 +Graph NN,0.2,0,0,0,20000,CPU,387.20843958854675,75.71038007736206 +GraphTM,0.2,10000,10.0,23,10,CUDA,169.7962884902954,77.56830601092896 +TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,987.0616261959076,20.116120218579233 +Graph NN,0.005,0,0,0,20000,CPU,455.586905002594,83.41529965400696 +GraphTM,0.005,10000,10.0,23,10,CUDA,109.7424705028534,98.5792349726776 +TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,998.4698519706726,76.81010928961749 +Graph NN,0.01,0,0,0,20000,CPU,466.44022035598755,98.52458834648132 +GraphTM,0.01,10000,10.0,23,10,CUDA,112.78495740890503,98.27868852459017 +TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,932.3163437843323,74.93169398907104 +Graph NN,0.02,0,0,0,20000,CPU,455.35024762153625,88.96175026893616 +GraphTM,0.02,10000,10.0,23,10,CUDA,120.741384267807,97.75956284153006 +TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,974.3740100860596,72.1311475409836 +Graph NN,0.05,0,0,0,20000,CPU,399.9565739631653,73.60655665397644 +GraphTM,0.05,10000,10.0,23,10,CUDA,136.17181992530823,94.67213114754098 +TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,971.1499485969543,64.1051912568306 +Graph NN,0.1,0,0,0,20000,CPU,447.5498752593994,70.8743155002594 +GraphTM,0.1,10000,10.0,23,10,CUDA,149.6928951740265,89.80874316939891 +TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,962.4737737178802,49.59016393442623 +Graph NN,0.2,0,0,0,20000,CPU,403.6350507736206,64.15300369262695 +GraphTM,0.2,10000,10.0,23,10,CUDA,170.02189421653748,78.16939890710383 +TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,947.2696743011475,20.116120218579233 +Graph NN,0.005,0,0,0,20000,CPU,470.0121097564697,81.20218515396118 +GraphTM,0.005,10000,10.0,23,10,CUDA,109.51706099510193,98.82513661202185 +TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,974.2360310554504,76.74180327868852 +Graph NN,0.01,0,0,0,20000,CPU,466.69573068618774,76.06557607650757 +GraphTM,0.01,10000,10.0,23,10,CUDA,112.95063591003418,98.4153005464481 +TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,948.407201051712,74.86338797814209 +Graph NN,0.02,0,0,0,20000,CPU,288.0073969364166,92.92349815368652 +GraphTM,0.02,10000,10.0,23,10,CUDA,119.34772634506226,97.48633879781421 +TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,971.228814125061,39.51502732240437 +Graph NN,0.05,0,0,0,20000,CPU,477.7228500843048,89.86338973045349 +GraphTM,0.05,10000,10.0,23,10,CUDA,135.2427453994751,94.86338797814207 +TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,964.5819866657257,34.56284153005464 +Graph NN,0.1,0,0,0,20000,CPU,459.15181946754456,71.22950553894043 +GraphTM,0.1,10000,10.0,23,10,CUDA,148.52941298484802,89.67213114754098 +TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,981.4810082912445,49.59016393442623 +Graph NN,0.2,0,0,0,20000,CPU,356.59899377822876,64.15300369262695 +GraphTM,0.2,10000,10.0,23,10,CUDA,169.7598683834076,76.85792349726775 +TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,959.9282560348511,20.116120218579233 +Graph NN,0.005,0,0,0,20000,CPU,378.94336581230164,80.32786846160889 +GraphTM,0.005,10000,10.0,23,10,CUDA,109.55144882202148,98.44262295081967 +TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,947.1284465789795,76.63934426229508 +Graph NN,0.01,0,0,0,20000,CPU,407.1111581325531,94.31694149971008 +GraphTM,0.01,10000,10.0,23,10,CUDA,112.06348276138306,98.27868852459017 +TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,941.711061000824,39.65163934426229 +Graph NN,0.02,0,0,0,20000,CPU,402.2970163822174,79.80874180793762 +GraphTM,0.02,10000,10.0,23,10,CUDA,120.20444130897522,97.8415300546448 +TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,998.2885782718658,72.1311475409836 +Graph NN,0.05,0,0,0,20000,CPU,400.97751235961914,85.30054688453674 +GraphTM,0.05,10000,10.0,23,10,CUDA,136.81029963493347,94.78142076502732 +TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,1003.2194263935089,64.1051912568306 +Graph NN,0.1,0,0,0,20000,CPU,413.25741934776306,74.59016442298889 +GraphTM,0.1,10000,10.0,23,10,CUDA,148.70455861091614,89.89071038251366 +TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,974.4099938869476,49.62431693989071 +Graph NN,0.2,0,0,0,20000,CPU,369.36416029930115,64.15300369262695 +GraphTM,0.2,10000,10.0,23,10,CUDA,170.01750564575195,78.55191256830601 +TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,990.2080008983612,20.184426229508194 +Graph NN,0.005,0,0,0,20000,CPU,440.5256702899933,90.4644787311554 +GraphTM,0.005,10000,10.0,23,10,CUDA,109.76434278488159,98.55191256830601 +TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,1004.704318523407,76.63934426229508 +Graph NN,0.01,0,0,0,20000,CPU,385.76011848449707,77.62295007705688 +GraphTM,0.01,10000,10.0,23,10,CUDA,113.28425002098083,98.44262295081967 +TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,953.8945541381836,74.93169398907104 +Graph NN,0.02,0,0,0,20000,CPU,422.2995481491089,90.71038365364075 +GraphTM,0.02,10000,10.0,23,10,CUDA,121.29091334342957,97.6775956284153 +TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,1002.099497795105,72.1311475409836 +Graph NN,0.05,0,0,0,20000,CPU,383.33483958244324,81.8306028842926 +GraphTM,0.05,10000,10.0,23,10,CUDA,134.72863698005676,94.53551912568307 +TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,936.831921339035,64.1051912568306 +Graph NN,0.1,0,0,0,20000,CPU,320.32143545150757,83.60655903816223 +GraphTM,0.1,10000,10.0,23,10,CUDA,150.56500816345215,89.15300546448087 +TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,955.8687121868134,49.62431693989071 +Graph NN,0.2,0,0,0,20000,CPU,432.34014868736267,64.15300369262695 +GraphTM,0.2,10000,10.0,23,10,CUDA,169.61127710342407,79.12568306010928 +TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,945.0617082118988,20.116120218579233 +Graph NN,0.005,0,0,0,20000,CPU,458.87039852142334,79.37158346176147 +GraphTM,0.005,10000,10.0,23,10,CUDA,110.9952290058136,98.82513661202185 +TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,985.8775904178619,76.63934426229508 +Graph NN,0.01,0,0,0,20000,CPU,453.55728340148926,76.06557607650757 +GraphTM,0.01,10000,10.0,23,10,CUDA,113.85269451141357,98.27868852459017 +TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,941.0662143230438,75.06830601092896 +Graph NN,0.02,0,0,0,20000,CPU,416.2407822608948,91.66666865348816 +GraphTM,0.02,10000,10.0,23,10,CUDA,120.69959592819214,97.78688524590164 +TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,973.9127674102783,72.40437158469946 +Graph NN,0.05,0,0,0,20000,CPU,311.2621831893921,75.46448111534119 +GraphTM,0.05,10000,10.0,23,10,CUDA,134.66055345535278,94.89071038251366 +TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,953.3380017280579,63.25136612021858 +Graph NN,0.1,0,0,0,20000,CPU,425.43416261672974,73.79781603813171 +GraphTM,0.1,10000,10.0,23,10,CUDA,150.67951107025146,90.27322404371586 +TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,967.5897221565247,49.59016393442623 +Graph NN,0.2,0,0,0,20000,CPU,379.8497235774994,64.15300369262695 +GraphTM,0.2,10000,10.0,23,10,CUDA,169.7126281261444,77.81420765027323 +TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,955.9427745342255,20.116120218579233 diff --git a/examples/recomm_system/experiment_results_old.csv b/examples/recomm_system/experiment_results_old.csv new file mode 100644 index 00000000..f715ba6a --- /dev/null +++ b/examples/recomm_system/experiment_results_old.csv @@ -0,0 +1,271 @@ +Algorithm,Noise_Ratio,T,s,Max_Included_Literals,Epochs,Platform,Total_Time,Accuracy +Graph NN,0.005,0,0,0,1000,CPU,0.03006434440612793,76.72131061553955 +GraphTM,0.005,10000,10.0,23,10,CUDA,34.547648191452026,98.46994535519126 +TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,89.6943154335022,76.63934426229508 +Graph NN,0.01,0,0,0,1000,CPU,0.01817464828491211,75.95628499984741 +GraphTM,0.01,10000,10.0,23,10,CUDA,34.95576763153076,98.44262295081967 +TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,96.10501098632812,74.93169398907104 +Graph NN,0.02,0,0,0,1000,CPU,0.03073263168334961,81.22950792312622 +GraphTM,0.02,10000,10.0,23,10,CUDA,36.0724892616272,97.43169398907104 +TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,95.67133641242981,72.40437158469946 +Graph NN,0.05,0,0,0,1000,CPU,0.014258623123168945,83.52459073066711 +GraphTM,0.05,10000,10.0,23,10,CUDA,38.86628317832947,95.0 +TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,96.7427487373352,64.65163934426229 +Graph NN,0.1,0,0,0,1000,CPU,0.022305965423583984,73.33333492279053 +GraphTM,0.1,10000,10.0,23,10,CUDA,37.45086216926575,90.08196721311475 +TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,90.45554423332214,49.8292349726776 +Graph NN,0.2,0,0,0,1000,CPU,0.03204679489135742,59.863388538360596 +GraphTM,0.2,10000,10.0,23,10,CUDA,16.268279790878296,78.77049180327869 +TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,96.16712856292725,20.184426229508194 +Graph NN,0.005,0,0,0,1000,CPU,0.0168764591217041,76.85792446136475 +GraphTM,0.005,10000,10.0,23,10,CUDA,31.40691065788269,98.82513661202185 +TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,88.05298614501953,76.74180327868852 +Graph NN,0.01,0,0,0,1000,CPU,0.01720118522644043,87.4316930770874 +GraphTM,0.01,10000,10.0,23,10,CUDA,31.529547214508057,98.4153005464481 +TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,89.19472336769104,74.93169398907104 +Graph NN,0.02,0,0,0,1000,CPU,0.014032602310180664,78.36065292358398 +GraphTM,0.02,10000,10.0,23,10,CUDA,32.8007595539093,97.62295081967213 +TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,94.56675243377686,72.6775956284153 +Graph NN,0.05,0,0,0,1000,CPU,0.016784191131591797,76.88524723052979 +GraphTM,0.05,10000,10.0,23,10,CUDA,34.84256434440613,94.75409836065573 +TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,96.4975814819336,64.1051912568306 +Graph NN,0.1,0,0,0,1000,CPU,0.014883041381835938,70.54644823074341 +GraphTM,0.1,10000,10.0,23,10,CUDA,36.750433683395386,89.97267759562841 +TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,96.35110449790955,50.341530054644814 +Graph NN,0.2,0,0,0,1000,CPU,0.03427433967590332,61.50273084640503 +GraphTM,0.2,10000,10.0,23,10,CUDA,39.63756251335144,79.01639344262294 +TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,97.00698733329773,20.116120218579233 +Graph NN,0.005,0,0,0,20000,CPU,370.7295939922333,87.5683069229126 +GraphTM,0.005,10000,10.0,23,10,CUDA,342.7878243923187,98.82513661202185 +TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,954.4101324081421,76.63934426229508 +Graph NN,0.01,0,0,0,20000,CPU,304.6031119823456,86.74863576889038 +GraphTM,0.01,10000,10.0,23,10,CUDA,346.8704605102539,98.25136612021858 +TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,978.3629264831543,74.93169398907104 +Graph NN,0.02,0,0,0,20000,CPU,403.2585175037384,75.30054450035095 +GraphTM,0.02,10000,10.0,23,10,CUDA,353.39254236221313,97.65027322404372 +TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,971.3300836086273,72.1311475409836 +Graph NN,0.05,0,0,0,20000,CPU,398.8085067272186,93.8524603843689 +GraphTM,0.05,10000,10.0,23,10,CUDA,368.16111874580383,94.59016393442623 +TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,960.4506890773773,63.661202185792355 +Graph NN,0.1,0,0,0,20000,CPU,388.4886665344238,75.43715834617615 +GraphTM,0.1,10000,10.0,23,10,CUDA,340.63327074050903,90.43715846994536 +TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,972.1077370643616,49.35109289617486 +Graph NN,0.2,0,0,0,20000,CPU,438.5506749153137,64.04371857643127 +GraphTM,0.2,10000,10.0,23,10,CUDA,357.2651107311249,77.89617486338798 +TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,948.7157049179077,20.116120218579233 +Graph NN,0.005,0,0,0,20000,CPU,335.8319003582001,94.97267603874207 +GraphTM,0.005,10000,10.0,23,10,CUDA,343.08735728263855,98.63387978142076 +TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,947.0340785980225,76.74180327868852 +Graph NN,0.01,0,0,0,20000,CPU,380.5575759410858,94.37158703804016 +GraphTM,0.01,10000,10.0,23,10,CUDA,346.9574134349823,98.4153005464481 +TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,948.3826260566711,74.93169398907104 +Graph NN,0.02,0,0,0,20000,CPU,317.0974416732788,80.6010901927948 +GraphTM,0.02,10000,10.0,23,10,CUDA,352.5908226966858,97.5136612021858 +TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,966.0719907283783,72.40437158469946 +Graph NN,0.05,0,0,0,20000,CPU,472.30924010276794,73.08743000030518 +GraphTM,0.05,10000,10.0,23,10,CUDA,352.63378834724426,94.18032786885246 +TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,959.5826976299286,64.31010928961749 +Graph NN,0.1,0,0,0,20000,CPU,461.1769962310791,82.45901465415955 +GraphTM,0.1,10000,10.0,23,10,CUDA,384.25392842292786,89.80874316939891 +TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,968.517664194107,49.62431693989071 +Graph NN,0.2,0,0,0,20000,CPU,338.83801436424255,61.39343976974487 +GraphTM,0.2,10000,10.0,23,10,CUDA,406.0366141796112,79.37158469945356 +TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,956.5074710845947,20.116120218579233 +Graph NN,0.005,0,0,0,20000,CPU,449.974244594574,99.07103776931763 +GraphTM,0.005,10000,10.0,23,10,CUDA,110.82642030715942,98.63387978142076 +TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,958.8415122032166,76.63934426229508 +Graph NN,0.01,0,0,0,20000,CPU,340.71677923202515,91.557377576828 +GraphTM,0.01,10000,10.0,23,10,CUDA,113.30413746833801,98.4153005464481 +TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,954.5596807003021,74.86338797814209 +Graph NN,0.02,0,0,0,20000,CPU,395.9958527088165,90.95628261566162 +GraphTM,0.02,10000,10.0,23,10,CUDA,120.9222981929779,97.8415300546448 +TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,969.4929764270782,72.40437158469946 +Graph NN,0.05,0,0,0,20000,CPU,480.05427837371826,84.83606576919556 +GraphTM,0.05,10000,10.0,23,10,CUDA,135.44805693626404,94.67213114754098 +TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,960.4112854003906,64.00273224043715 +Graph NN,0.1,0,0,0,20000,CPU,383.12051796913147,70.8743155002594 +GraphTM,0.1,10000,10.0,23,10,CUDA,149.93119883537292,89.86338797814207 +TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,951.469316482544,49.35109289617486 +Graph NN,0.2,0,0,0,20000,CPU,463.9883725643158,66.22951030731201 +GraphTM,0.2,10000,10.0,23,10,CUDA,170.47470378875732,78.16939890710383 +TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,960.5258178710938,20.116120218579233 +Graph NN,0.005,0,0,0,20000,CPU,475.9830324649811,82.54098296165466 +GraphTM,0.005,10000,10.0,23,10,CUDA,109.21395993232727,98.7431693989071 +TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,1007.2876415252686,76.74180327868852 +Graph NN,0.01,0,0,0,20000,CPU,383.468213558197,84.89071130752563 +GraphTM,0.01,10000,10.0,23,10,CUDA,112.81892561912537,98.16939890710383 +TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,1012.9538216590881,75.0 +Graph NN,0.02,0,0,0,20000,CPU,420.129834651947,78.87977957725525 +GraphTM,0.02,10000,10.0,23,10,CUDA,120.55745768547058,97.75956284153006 +TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,1015.7262468338013,72.43852459016394 +Graph NN,0.05,0,0,0,20000,CPU,402.9082715511322,88.90710473060608 +GraphTM,0.05,10000,10.0,23,10,CUDA,136.1779272556305,94.69945355191257 +TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,1003.5450174808502,64.1051912568306 +Graph NN,0.1,0,0,0,20000,CPU,465.9741690158844,71.61202430725098 +GraphTM,0.1,10000,10.0,23,10,CUDA,150.92307353019714,90.21857923497268 +TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,993.3001370429993,49.62431693989071 +Graph NN,0.2,0,0,0,20000,CPU,477.5556457042694,61.967211961746216 +GraphTM,0.2,10000,10.0,23,10,CUDA,170.91576671600342,78.71584699453553 +TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,968.9711816310883,20.116120218579233 +Graph NN,0.005,0,0,0,20000,CPU,432.9368100166321,87.9781424999237 +GraphTM,0.005,10000,10.0,23,10,CUDA,110.05442261695862,98.4153005464481 +TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,996.241945028305,77.11748633879782 +Graph NN,0.01,0,0,0,20000,CPU,487.0275945663452,76.06557607650757 +GraphTM,0.01,10000,10.0,23,10,CUDA,114.20750546455383,98.27868852459017 +TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,965.2801012992859,74.89754098360656 +Graph NN,0.02,0,0,0,20000,CPU,469.96120142936707,84.61748361587524 +GraphTM,0.02,10000,10.0,23,10,CUDA,120.18490934371948,97.62295081967213 +TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,996.0747539997101,73.05327868852459 +Graph NN,0.05,0,0,0,20000,CPU,391.52739334106445,94.4535493850708 +GraphTM,0.05,10000,10.0,23,10,CUDA,135.62234830856323,94.89071038251366 +TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,1008.111634016037,64.00273224043715 +Graph NN,0.1,0,0,0,20000,CPU,393.4089164733887,82.24043846130371 +GraphTM,0.1,10000,10.0,23,10,CUDA,150.06821942329407,90.21857923497268 +TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,1029.8733656406403,46.89207650273224 +Graph NN,0.2,0,0,0,20000,CPU,457.90059518814087,64.50819969177246 +GraphTM,0.2,10000,10.0,23,10,CUDA,169.8122251033783,78.5792349726776 +TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,994.4631915092468,20.116120218579233 +Graph NN,0.005,0,0,0,20000,CPU,468.43779039382935,93.66120100021362 +GraphTM,0.005,10000,10.0,23,10,CUDA,791.0080873966217,98.66120218579235 +TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,1003.8278872966766,76.63934426229508 +Graph NN,0.01,0,0,0,20000,CPU,432.6524693965912,76.06557607650757 +GraphTM,0.01,10000,10.0,23,10,CUDA,114.20011568069458,98.4153005464481 +TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,1002.5212485790253,74.55601092896174 +Graph NN,0.02,0,0,0,20000,CPU,369.3357195854187,77.92349457740784 +GraphTM,0.02,10000,10.0,23,10,CUDA,120.16606998443604,97.78688524590164 +TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,1012.7241668701172,72.40437158469946 +Graph NN,0.05,0,0,0,20000,CPU,425.18350195884705,73.49726557731628 +GraphTM,0.05,10000,10.0,23,10,CUDA,134.74739480018616,94.53551912568307 +TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,989.5920696258545,64.00273224043715 +Graph NN,0.1,0,0,0,20000,CPU,490.52463579177856,74.23497438430786 +GraphTM,0.1,10000,10.0,23,10,CUDA,150.663067817688,90.05464480874316 +TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,1006.5979704856873,49.86338797814208 +Graph NN,0.2,0,0,0,20000,CPU,430.0901610851288,55.51912784576416 +GraphTM,0.2,10000,10.0,23,10,CUDA,169.53561758995056,78.52459016393442 +TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,978.9952318668365,20.116120218579233 +Graph NN,0.005,0,0,0,20000,CPU,348.87419414520264,88.87978196144104 +GraphTM,0.005,10000,10.0,23,10,CUDA,110.35069704055786,98.49726775956285 +TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,1027.553718328476,76.63934426229508 +Graph NN,0.01,0,0,0,20000,CPU,459.8675227165222,94.97267603874207 +GraphTM,0.01,10000,10.0,23,10,CUDA,113.4369592666626,98.4153005464481 +TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,1020.3086180686951,74.93169398907104 +Graph NN,0.02,0,0,0,20000,CPU,402.3793728351593,98.08743000030518 +GraphTM,0.02,10000,10.0,23,10,CUDA,121.04798412322998,97.78688524590164 +TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,1001.1654710769653,72.1311475409836 +Graph NN,0.05,0,0,0,20000,CPU,372.8648886680603,77.81420946121216 +GraphTM,0.05,10000,10.0,23,10,CUDA,135.42569255828857,94.78142076502732 +TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,1007.9532639980316,64.31010928961749 +Graph NN,0.1,0,0,0,20000,CPU,379.2149317264557,88.55191469192505 +GraphTM,0.1,10000,10.0,23,10,CUDA,149.3813440799713,89.50819672131148 +TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,1043.259267091751,49.65846994535519 +Graph NN,0.2,0,0,0,20000,CPU,327.1461730003357,64.15300369262695 +GraphTM,0.2,10000,10.0,23,10,CUDA,169.63331365585327,77.75956284153006 +TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,1010.9232707023621,20.081967213114755 +Graph NN,0.005,0,0,0,20000,CPU,365.3540139198303,84.56284403800964 +GraphTM,0.005,10000,10.0,23,10,CUDA,109.86703443527222,98.55191256830601 +TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,985.2240512371063,76.84426229508196 +Graph NN,0.01,0,0,0,20000,CPU,419.19047832489014,90.65573811531067 +GraphTM,0.01,10000,10.0,23,10,CUDA,113.7970187664032,98.19672131147541 +TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,1064.9949398040771,75.27322404371584 +Graph NN,0.02,0,0,0,20000,CPU,331.5898778438568,82.13114738464355 +GraphTM,0.02,10000,10.0,23,10,CUDA,120.9221625328064,97.8415300546448 +TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,995.9801988601685,72.1311475409836 +Graph NN,0.05,0,0,0,20000,CPU,471.61706471443176,76.4207661151886 +GraphTM,0.05,10000,10.0,23,10,CUDA,135.71256685256958,94.31693989071039 +TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,1049.4032156467438,64.00273224043715 +Graph NN,0.1,0,0,0,20000,CPU,408.78746509552,75.76502561569214 +GraphTM,0.1,10000,10.0,23,10,CUDA,150.7326798439026,89.86338797814207 +TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,1033.6956369876862,49.59016393442623 +Graph NN,0.2,0,0,0,20000,CPU,606.6176900863647,75.84699392318726 +GraphTM,0.2,10000,10.0,23,10,CUDA,767.3086304664612,79.18032786885246 +TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,1133.7219278812408,20.116120218579233 +Graph NN,0.005,0,0,0,20000,CPU,581.8730342388153,75.68305730819702 +GraphTM,0.005,10000,10.0,23,10,CUDA,331.4337913990021,98.68852459016394 +TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,1139.0209171772003,76.63934426229508 +Graph NN,0.01,0,0,0,20000,CPU,625.7649390697479,79.91803288459778 +GraphTM,0.01,10000,10.0,23,10,CUDA,390.8302972316742,98.27868852459017 +TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,1126.1463103294373,74.86338797814209 +Graph NN,0.02,0,0,0,20000,CPU,400.4486656188965,88.87978196144104 +GraphTM,0.02,10000,10.0,23,10,CUDA,1433.5869204998016,97.73224043715847 +TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,950.7241444587708,72.43852459016394 +Graph NN,0.05,0,0,0,20000,CPU,425.54064321517944,88.22404146194458 +GraphTM,0.05,10000,10.0,23,10,CUDA,135.85678553581238,95.0 +TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,980.2039513587952,40.26639344262295 +Graph NN,0.1,0,0,0,20000,CPU,452.5277452468872,75.38251280784607 +GraphTM,0.1,10000,10.0,23,10,CUDA,149.88782930374146,89.80874316939891 +TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,1026.7852320671082,49.59016393442623 +Graph NN,0.2,0,0,0,20000,CPU,470.88474774360657,69.67213153839111 +GraphTM,0.2,10000,10.0,23,10,CUDA,169.65682435035706,78.38797814207649 +TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,1025.9789564609528,20.116120218579233 +Graph NN,0.005,0,0,0,20000,CPU,415.4326367378235,75.68305730819702 +GraphTM,0.005,10000,10.0,23,10,CUDA,109.78167200088501,98.82513661202185 +TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,1003.7237763404846,76.63934426229508 +Graph NN,0.01,0,0,0,20000,CPU,444.45101857185364,92.65027046203613 +GraphTM,0.01,10000,10.0,23,10,CUDA,112.90761637687683,98.14207650273225 +TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,952.2491714954376,74.86338797814209 +Graph NN,0.02,0,0,0,20000,CPU,320.37370920181274,93.90710592269897 +GraphTM,0.02,10000,10.0,23,10,CUDA,119.93352174758911,97.78688524590164 +TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,943.684113740921,72.40437158469946 +Graph NN,0.05,0,0,0,20000,CPU,481.5506682395935,73.08743000030518 +GraphTM,0.05,10000,10.0,23,10,CUDA,135.72563362121582,94.75409836065573 +TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,1023.0283312797546,63.661202185792355 +Graph NN,0.1,0,0,0,20000,CPU,493.5546169281006,70.92896103858948 +GraphTM,0.1,10000,10.0,23,10,CUDA,149.45619106292725,89.80874316939891 +TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,1015.6581709384918,49.62431693989071 +Graph NN,0.2,0,0,0,20000,CPU,413.9959945678711,64.15300369262695 +GraphTM,0.2,10000,10.0,23,10,CUDA,170.9294879436493,78.77049180327869 +TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,986.7937209606171,20.116120218579233 +Graph NN,0.005,0,0,0,20000,CPU,378.53097796440125,75.68305730819702 +GraphTM,0.005,10000,10.0,23,10,CUDA,110.98681783676147,98.30601092896175 +TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,1019.9160070419312,76.74180327868852 +Graph NN,0.01,0,0,0,20000,CPU,474.00093841552734,91.0109281539917 +GraphTM,0.01,10000,10.0,23,10,CUDA,111.94242978096008,98.4153005464481 +TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,999.8850407600403,75.03415300546447 +Graph NN,0.02,0,0,0,20000,CPU,346.5858099460602,79.3169379234314 +GraphTM,0.02,10000,10.0,23,10,CUDA,120.32013273239136,97.81420765027322 +TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,937.4906117916107,71.92622950819673 +Graph NN,0.05,0,0,0,20000,CPU,408.48123002052307,79.61748838424683 +GraphTM,0.05,10000,10.0,23,10,CUDA,134.27622246742249,94.72677595628414 +TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,998.1966772079468,64.1051912568306 +Graph NN,0.1,0,0,0,20000,CPU,373.10851979255676,70.8743155002594 +GraphTM,0.1,10000,10.0,23,10,CUDA,148.95248794555664,89.86338797814207 +TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,993.9887461662292,49.62431693989071 +Graph NN,0.2,0,0,0,20000,CPU,388.21142077445984,64.15300369262695 +GraphTM,0.2,10000,10.0,23,10,CUDA,168.77049660682678,76.93989071038251 +TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,947.7270972728729,20.116120218579233 +Graph NN,0.005,0,0,0,20000,CPU,370.7274992465973,75.79234838485718 +GraphTM,0.005,10000,10.0,23,10,CUDA,109.92479467391968,98.27868852459017 +TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,944.8954434394836,76.74180327868852 +Graph NN,0.01,0,0,0,20000,CPU,382.68008041381836,90.8196747303009 +GraphTM,0.01,10000,10.0,23,10,CUDA,113.02455401420593,98.4153005464481 +TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,958.4739623069763,74.86338797814209 +Graph NN,0.02,0,0,0,20000,CPU,466.3325071334839,96.22950553894043 +GraphTM,0.02,10000,10.0,23,10,CUDA,121.06816530227661,97.6775956284153 +TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,952.7009084224701,72.1311475409836 +Graph NN,0.05,0,0,0,20000,CPU,462.6835868358612,75.79234838485718 +GraphTM,0.05,10000,10.0,23,10,CUDA,136.21898555755615,94.53551912568307 +TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,974.2475302219391,64.00273224043715 +Graph NN,0.1,0,0,0,20000,CPU,425.79654932022095,87.18579411506653 +GraphTM,0.1,10000,10.0,23,10,CUDA,149.70053339004517,90.0 +TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,1005.1148529052734,49.59016393442623 +Graph NN,0.2,0,0,0,20000,CPU,454.7309219837189,60.10928750038147 +GraphTM,0.2,10000,10.0,23,10,CUDA,170.75228261947632,78.68852459016394 +TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,949.2937788963318,20.116120218579233 +Graph NN,0.005,0,0,0,20000,CPU,570.247394323349,75.68305730819702 +GraphTM,0.005,10000,10.0,23,10,CUDA,478.04068207740784,98.5792349726776 +TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,1043.9900722503662,76.22950819672131 +Graph NN,0.01,0,0,0,20000,CPU,428.5804445743561,98.68852496147156 +GraphTM,0.01,10000,10.0,23,10,CUDA,522.4638862609863,98.44262295081967 +TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,1060.4919381141663,74.93169398907104 +Graph NN,0.02,0,0,0,20000,CPU,432.5051038265228,76.25682950019836 +GraphTM,0.02,10000,10.0,23,10,CUDA,465.56538343429565,97.73224043715847 +TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,1074.2418582439423,72.40437158469946 +Graph NN,0.05,0,0,0,20000,CPU,492.7251534461975,85.16393303871155 +GraphTM,0.05,10000,10.0,23,10,CUDA,688.4105927944183,94.91803278688525 +TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,1055.8136265277863,64.00273224043715 +Graph NN,0.1,0,0,0,20000,CPU,584.2829260826111,78.22404503822327 +GraphTM,0.1,10000,10.0,23,10,CUDA,625.4286091327667,90.13661202185791 +TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,1055.7545056343079,48.83879781420765 +Graph NN,0.2,0,0,0,20000,CPU,318.2997555732727,67.40437150001526 +GraphTM,0.2,10000,10.0,23,10,CUDA,1264.404123544693,77.62295081967213 +TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,1000.3779845237732,20.081967213114755 diff --git a/examples/recomm_system/graph_tm.py b/examples/recomm_system/graph_tm.py index d1464c75..d03d3be9 100644 --- a/examples/recomm_system/graph_tm.py +++ b/examples/recomm_system/graph_tm.py @@ -14,8 +14,14 @@ def main(args): x, y = prepare_dataset.construct_x_y(data) X_train, X_test, Y_train, Y_test = prepare_dataset.train_test_split(x,y) users = data['user_id'].unique() + print("Users: ",len(users)) + items = data['product_id'].unique() + print("Items: ",len(items)) + categories = data['category'].unique() + print("Categories: ",len(categories)) + # Initialize Graphs with symbols for GTM number_of_nodes = 3 symbols = [] diff --git a/examples/recomm_system/main.sh b/examples/recomm_system/main.sh index 8c7a22ad..a5db0425 100644 --- a/examples/recomm_system/main.sh +++ b/examples/recomm_system/main.sh @@ -1,16 +1,26 @@ +#!/bin/bash + echo `date`, Setup the environment ... set -e # exit if error models="graph_tm tm_classifier graph_nn" dataset_noise_ratios="0.005 0.01 0.02 0.05 0.1 0.2" +num_iterations=10 # Number of times to repeat the experiments + +for (( i=1; i<=num_iterations; i++ )) +do + echo "Iteration $i of $num_iterations" + + for N in $dataset_noise_ratios; do + echo `date`, Running Graph NN ... + python3 graph_nn.py --dataset_noise_ratio $N + + echo `date`, Running Graph Tsetlin Machine ... + python3 graph_tm.py --dataset_noise_ratio $N + + echo `date`, Running Tsetlin Machine Classifier ... + python3 tm_classifier.py --dataset_noise_ratio $N + done +done -for N in $dataset_noise_ratios; do - echo `date`, Running Graph NN ... - python3 graph_nn.py --dataset_noise_ratio $N - echo `date`, Running Graph Tsetlin Machine ... - python3 graph_tm.py --dataset_noise_ratio $N - - echo `date`, Running Tsetlin Machine Classifier ... - python3 tm_classifier.py --dataset_noise_ratio $N -done \ No newline at end of file diff --git a/examples/recomm_system/test.ipynb b/examples/recomm_system/test.ipynb new file mode 100644 index 00000000..4e44624f --- /dev/null +++ b/examples/recomm_system/test.ipynb @@ -0,0 +1,186 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Creating training data\n", + "Warning: Looks like you're using an outdated `kagglehub` version (installed: 0.3.5), please consider upgrading to the latest version (0.3.6).\n", + "Path to dataset files: /root/.cache/kagglehub/datasets/karkavelrajaj/amazon-sales-dataset/versions/1\n", + "Original data shape: (1465, 16)\n", + "Expanded data shape: (14640, 4)\n", + "Dataset saved to noisy_dataset_0.005.csv\n", + "Creating training data\n", + "Warning: Looks like you're using an outdated `kagglehub` version (installed: 0.3.5), please consider upgrading to the latest version (0.3.6).\n", + "Path to dataset files: /root/.cache/kagglehub/datasets/karkavelrajaj/amazon-sales-dataset/versions/1\n", + "Original data shape: (1465, 16)\n", + "Expanded data shape: (14640, 4)\n", + "Dataset saved to noisy_dataset_0.01.csv\n", + "Creating training data\n", + "Warning: Looks like you're using an outdated `kagglehub` version (installed: 0.3.5), please consider upgrading to the latest version (0.3.6).\n", + "Path to dataset files: /root/.cache/kagglehub/datasets/karkavelrajaj/amazon-sales-dataset/versions/1\n", + "Original data shape: (1465, 16)\n", + "Expanded data shape: (14640, 4)\n", + "Dataset saved to noisy_dataset_0.02.csv\n", + "Creating training data\n", + "Warning: Looks like you're using an outdated `kagglehub` version (installed: 0.3.5), please consider upgrading to the latest version (0.3.6).\n", + "Path to dataset files: /root/.cache/kagglehub/datasets/karkavelrajaj/amazon-sales-dataset/versions/1\n", + "Original data shape: (1465, 16)\n", + "Expanded data shape: (14640, 4)\n", + "Dataset saved to noisy_dataset_0.05.csv\n", + "Creating training data\n", + "Warning: Looks like you're using an outdated `kagglehub` version (installed: 0.3.5), please consider upgrading to the latest version (0.3.6).\n", + "Path to dataset files: /root/.cache/kagglehub/datasets/karkavelrajaj/amazon-sales-dataset/versions/1\n", + "Original data shape: (1465, 16)\n", + "Expanded data shape: (14640, 4)\n", + "Dataset saved to noisy_dataset_0.1.csv\n", + "Creating training data\n", + "Warning: Looks like you're using an outdated `kagglehub` version (installed: 0.3.5), please consider upgrading to the latest version (0.3.6).\n", + "Path to dataset files: /root/.cache/kagglehub/datasets/karkavelrajaj/amazon-sales-dataset/versions/1\n", + "Original data shape: (1465, 16)\n", + "Expanded data shape: (14640, 4)\n", + "Dataset saved to noisy_dataset_0.2.csv\n" + ] + } + ], + "source": [ + "import prepare_dataset\n", + "import pandas as pd\n", + "import os\n", + "\n", + "dataset_noise_ratios = [0.005,0.01,0.02,0.05,0.1,0.2]\n", + "for noise in dataset_noise_ratios:\n", + " data = prepare_dataset.aug_amazon_products(noise_ratio = noise)\n", + " df = pd.DataFrame(data)\n", + " noise_dataset_file = f\"noisy_dataset_{noise}.csv\"\n", + " if os.path.exists(noise_dataset_file):\n", + " df.to_csv(noise_dataset_file, mode='a', index=False, header=False)\n", + " else:\n", + " df.to_csv(noise_dataset_file, index=False)\n", + " print(f\"Dataset saved to {noise_dataset_file}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " Algorithm Noise_Ratio T s Max_Included_Literals Epochs \\\n", + "0 Graph NN 0.005 0 0.0 0 20000 \n", + "1 GraphTM 0.005 10000 10.0 23 10 \n", + "2 TMClassifier 0.005 10000 10.0 32 10 \n", + "3 Graph NN 0.010 0 0.0 0 20000 \n", + "4 GraphTM 0.010 10000 10.0 23 10 \n", + ".. ... ... ... ... ... ... \n", + "175 GraphTM 0.100 10000 10.0 23 10 \n", + "176 TMClassifier 0.100 10000 10.0 32 10 \n", + "177 Graph NN 0.200 0 0.0 0 20000 \n", + "178 GraphTM 0.200 10000 10.0 23 10 \n", + "179 TMClassifier 0.200 10000 10.0 32 10 \n", + "\n", + " Platform Total_Time Accuracy \n", + "0 CPU 418.925047 75.628418 \n", + "1 CUDA 110.356832 98.688525 \n", + "2 CPU_sparse 1059.163476 76.810109 \n", + "3 CPU 550.698057 94.508195 \n", + "4 CUDA 114.062763 98.415301 \n", + ".. ... ... ... \n", + "175 CUDA 150.679511 90.273224 \n", + "176 CPU_sparse 967.589722 49.590164 \n", + "177 CPU 379.849724 64.153004 \n", + "178 CUDA 169.712628 77.814208 \n", + "179 CPU_sparse 955.942775 20.116120 \n", + "\n", + "[180 rows x 9 columns]\n", + "\n", + "\\begin{table}[h!]\n", + "\\centering\n", + "\\begin{tabular}{|c|c|c|c|}\n", + "\\hline\n", + "\\textbf{Noise Ratio} & \\textbf{GCN (\\%)} & \\textbf{GTM (\\%)} & \\textbf{TMClassifier (\\%)} \\\\ \\hline\n", + "0.005 & 81.73 & 98.62 & 76.70 \\\\ \\hline\n", + "0.01 & 84.73 & 98.34 & 71.43 \\\\ \\hline\n", + "0.02 & 87.81 & 97.76 & 69.09 \\\\ \\hline\n", + "0.05 & 79.86 & 94.74 & 61.04 \\\\ \\hline\n", + "0.1 & 76.40 & 89.82 & 49.60 \\\\ \\hline\n", + "0.2 & 65.90 & 78.22 & 20.12 \\\\ \\hline\n", + "\\end{tabular}\n", + "\\caption{Average accuracy comparison of GCN, GraphTM, and TMClassifier for varying noise ratios.}\n", + "\\label{tab:recomm_sys_accuracy}\n", + "\\end{table}\n" + ] + } + ], + "source": [ + "import pandas as pd\n", + "data = pd.read_csv(\"experiment_results.csv\")\n", + "\n", + "# Extract records within the specified range, e.g., rows 3 to 5 (0-indexed)\n", + "# This assumes each algorithm data spans three consecutive rows\n", + "start_index = 0\n", + "range_records = data.iloc[start_index:len(data)]\n", + "print(range_records)\n", + "# Create a dictionary to store the accuracy values\n", + "noise_accuracies = {}\n", + "\n", + "# Algorithm,Noise_Ratio,T,s,Max_Included_Literals,Epochs,Platform,Total_Time,Accuracy\n", + "# Group the data by Algorithm and Noise Ratio to calculate average accuracies\n", + "grouped_data = data.groupby(['Algorithm', 'Noise_Ratio']).agg({'Accuracy': 'mean'}).reset_index()\n", + "\n", + "# Pivot the data to get a structure suitable for LaTeX table generation\n", + "pivot_data = grouped_data.pivot(index='Noise_Ratio', columns='Algorithm', values='Accuracy')\n", + " \n", + "# Generate LaTeX table\n", + "latex_table = \"\"\"\n", + "\\\\begin{table}[h!]\n", + "\\\\centering\n", + "\\\\begin{tabular}{|c|c|c|c|}\n", + "\\\\hline\n", + "\\\\textbf{Noise Ratio} & \\\\textbf{GCN (\\\\%)} & \\\\textbf{GTM (\\\\%)} & \\\\textbf{TMClassifier (\\\\%)} \\\\\\\\ \\\\hline\n", + "\"\"\"\n", + "\n", + "# Iterate over the pivot data to construct the table rows\n", + "for noise_ratio, row in pivot_data.iterrows():\n", + " latex_table += f\"{noise_ratio} & \"\n", + " latex_table += f\"{row['Graph NN']:.2f} & {row['GraphTM']:.2f} & {row['TMClassifier']:.2f} \\\\\\\\ \\\\hline\\n\"\n", + "\n", + "latex_table += \"\\\\end{tabular}\\n\"\n", + "latex_table += \"\\\\caption{Average accuracy comparison of GCN, GraphTM, and TMClassifier for varying noise ratios.}\\n\"\n", + "latex_table += \"\\\\label{tab:recomm_sys_accuracy}\\n\"\n", + "latex_table += \"\\\\end{table}\"\n", + "\n", + "print(latex_table)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} From 26e208394e7dfd0d2dbd3cf0f9ce16e4457916fb Mon Sep 17 00:00:00 2001 From: Ahmed Khalid Date: Tue, 25 Feb 2025 08:54:16 +0000 Subject: [PATCH 24/33] adding exp id --- examples/recomm_system/experiment_results.csv | 183 +----------------- .../experiment_results_ensamble.csv | 181 +++++++++++++++++ examples/recomm_system/graph_nn.py | 4 +- examples/recomm_system/graph_tm.py | 2 + examples/recomm_system/main.sh | 9 +- examples/recomm_system/test.ipynb | 31 +-- examples/recomm_system/tm_classifier.py | 2 + 7 files changed, 198 insertions(+), 214 deletions(-) create mode 100644 examples/recomm_system/experiment_results_ensamble.csv diff --git a/examples/recomm_system/experiment_results.csv b/examples/recomm_system/experiment_results.csv index b394dad6..09e4f8e7 100644 --- a/examples/recomm_system/experiment_results.csv +++ b/examples/recomm_system/experiment_results.csv @@ -1,181 +1,2 @@ -Algorithm,Noise_Ratio,T,s,Max_Included_Literals,Epochs,Platform,Total_Time,Accuracy -Graph NN,0.005,0,0,0,20000,CPU,418.9250466823578,75.62841773033142 -GraphTM,0.005,10000,10.0,23,10,CUDA,110.35683226585388,98.68852459016394 -TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,1059.1634759902954,76.81010928961749 -Graph NN,0.01,0,0,0,20000,CPU,550.6980571746826,94.50819492340088 -GraphTM,0.01,10000,10.0,23,10,CUDA,114.06276345252991,98.4153005464481 -TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,1051.916612625122,75.3415300546448 -Graph NN,0.02,0,0,0,20000,CPU,475.44024682044983,75.30054450035095 -GraphTM,0.02,10000,10.0,23,10,CUDA,121.55624794960022,97.8415300546448 -TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,1043.9487817287445,72.1311475409836 -Graph NN,0.05,0,0,0,20000,CPU,411.8552327156067,80.98360896110535 -GraphTM,0.05,10000,10.0,23,10,CUDA,136.7814338207245,94.20765027322405 -TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,1044.2656917572021,64.00273224043715 -Graph NN,0.1,0,0,0,20000,CPU,484.6550889015198,68.7158465385437 -GraphTM,0.1,10000,10.0,23,10,CUDA,150.34457921981812,89.72677595628416 -TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,1061.191523551941,49.62431693989071 -Graph NN,0.2,0,0,0,20000,CPU,483.8463816642761,71.28415107727051 -GraphTM,0.2,10000,10.0,23,10,CUDA,169.18810439109802,78.49726775956285 -TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,1071.927158355713,20.116120218579233 -Graph NN,0.005,0,0,0,20000,CPU,473.5806052684784,86.36612296104431 -GraphTM,0.005,10000,10.0,23,10,CUDA,110.18979954719543,98.60655737704917 -TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,979.0509588718414,76.74180327868852 -Graph NN,0.01,0,0,0,20000,CPU,444.6897065639496,93.55190992355347 -GraphTM,0.01,10000,10.0,23,10,CUDA,112.48035550117493,98.4153005464481 -TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,1007.9654748439789,74.86338797814209 -Graph NN,0.02,0,0,0,20000,CPU,386.32835030555725,93.22404265403748 -GraphTM,0.02,10000,10.0,23,10,CUDA,120.46316766738892,97.73224043715847 -TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,1017.5866801738739,73.25819672131148 -Graph NN,0.05,0,0,0,20000,CPU,417.78410935401917,73.1693983078003 -GraphTM,0.05,10000,10.0,23,10,CUDA,135.64952206611633,95.08196721311475 -TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,945.0465729236603,64.00273224043715 -Graph NN,0.1,0,0,0,20000,CPU,481.6537721157074,77.18579173088074 -GraphTM,0.1,10000,10.0,23,10,CUDA,149.57958960533142,90.08196721311475 -TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,938.0212676525116,49.59016393442623 -Graph NN,0.2,0,0,0,20000,CPU,391.36059975624084,64.15300369262695 -GraphTM,0.2,10000,10.0,23,10,CUDA,169.49347591400146,77.65027322404372 -TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,940.9758951663971,20.116120218579233 -Graph NN,0.005,0,0,0,20000,CPU,480.5005066394806,75.68305730819702 -GraphTM,0.005,10000,10.0,23,10,CUDA,109.65927052497864,98.19672131147541 -TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,947.7581994533539,76.63934426229508 -Graph NN,0.01,0,0,0,20000,CPU,449.22584795951843,76.36612057685852 -GraphTM,0.01,10000,10.0,23,10,CUDA,113.07226181030273,98.27868852459017 -TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,1010.8711988925934,74.93169398907104 -Graph NN,0.02,0,0,0,20000,CPU,403.96647000312805,96.85792326927185 -GraphTM,0.02,10000,10.0,23,10,CUDA,120.02044725418091,97.78688524590164 -TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,1011.7896072864532,72.40437158469946 -Graph NN,0.05,0,0,0,20000,CPU,460.688773393631,85.00000238418579 -GraphTM,0.05,10000,10.0,23,10,CUDA,136.04891228675842,94.69945355191257 -TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,1014.1492829322815,64.00273224043715 -Graph NN,0.1,0,0,0,20000,CPU,407.9346880912781,74.1256833076477 -GraphTM,0.1,10000,10.0,23,10,CUDA,149.6586093902588,90.08196721311475 -TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,990.8282098770142,49.59016393442623 -Graph NN,0.2,0,0,0,20000,CPU,437.8108870983124,65.60109257698059 -GraphTM,0.2,10000,10.0,23,10,CUDA,168.44772601127625,78.93442622950819 -TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,1022.1848647594452,20.116120218579233 -Graph NN,0.005,0,0,0,20000,CPU,430.3925087451935,89.20764923095703 -GraphTM,0.005,10000,10.0,23,10,CUDA,109.6658935546875,98.68852459016394 -TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,1016.199923992157,76.63934426229508 -Graph NN,0.01,0,0,0,20000,CPU,396.3338620662689,84.23497080802917 -GraphTM,0.01,10000,10.0,23,10,CUDA,113.67849016189575,98.27868852459017 -TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,944.4602844715118,74.86338797814209 -Graph NN,0.02,0,0,0,20000,CPU,434.91951632499695,93.25136542320251 -GraphTM,0.02,10000,10.0,23,10,CUDA,120.31921482086182,97.8415300546448 -TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,933.2245874404907,72.40437158469946 -Graph NN,0.05,0,0,0,20000,CPU,483.2671537399292,80.32786846160889 -GraphTM,0.05,10000,10.0,23,10,CUDA,135.68922591209412,94.78142076502732 -TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,994.6384744644165,64.13934426229508 -Graph NN,0.1,0,0,0,20000,CPU,424.9935986995697,81.33879899978638 -GraphTM,0.1,10000,10.0,23,10,CUDA,149.08107113838196,89.59016393442623 -TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,944.0273253917694,49.62431693989071 -Graph NN,0.2,0,0,0,20000,CPU,333.49274706840515,61.50273084640503 -GraphTM,0.2,10000,10.0,23,10,CUDA,170.906751871109,78.98907103825137 -TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,965.9725024700165,20.116120218579233 -Graph NN,0.005,0,0,0,20000,CPU,377.28471970558167,75.68305730819702 -GraphTM,0.005,10000,10.0,23,10,CUDA,109.61631536483765,98.82513661202185 -TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,976.8008840084076,76.67349726775956 -Graph NN,0.01,0,0,0,20000,CPU,473.2922372817993,76.06557607650757 -GraphTM,0.01,10000,10.0,23,10,CUDA,112.87212014198303,98.27868852459017 -TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,942.7254059314728,74.86338797814209 -Graph NN,0.02,0,0,0,20000,CPU,357.36573815345764,75.40983557701111 -GraphTM,0.02,10000,10.0,23,10,CUDA,119.41612005233765,97.8415300546448 -TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,985.81947016716,72.40437158469946 -Graph NN,0.05,0,0,0,20000,CPU,440.75843334198,73.08743000030518 -GraphTM,0.05,10000,10.0,23,10,CUDA,136.8215868473053,94.91803278688525 -TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,997.739678144455,64.1051912568306 -Graph NN,0.1,0,0,0,20000,CPU,426.73446226119995,88.55191469192505 -GraphTM,0.1,10000,10.0,23,10,CUDA,149.54467248916626,89.94535519125682 -TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,980.096907377243,49.59016393442623 -Graph NN,0.2,0,0,0,20000,CPU,387.20843958854675,75.71038007736206 -GraphTM,0.2,10000,10.0,23,10,CUDA,169.7962884902954,77.56830601092896 -TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,987.0616261959076,20.116120218579233 -Graph NN,0.005,0,0,0,20000,CPU,455.586905002594,83.41529965400696 -GraphTM,0.005,10000,10.0,23,10,CUDA,109.7424705028534,98.5792349726776 -TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,998.4698519706726,76.81010928961749 -Graph NN,0.01,0,0,0,20000,CPU,466.44022035598755,98.52458834648132 -GraphTM,0.01,10000,10.0,23,10,CUDA,112.78495740890503,98.27868852459017 -TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,932.3163437843323,74.93169398907104 -Graph NN,0.02,0,0,0,20000,CPU,455.35024762153625,88.96175026893616 -GraphTM,0.02,10000,10.0,23,10,CUDA,120.741384267807,97.75956284153006 -TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,974.3740100860596,72.1311475409836 -Graph NN,0.05,0,0,0,20000,CPU,399.9565739631653,73.60655665397644 -GraphTM,0.05,10000,10.0,23,10,CUDA,136.17181992530823,94.67213114754098 -TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,971.1499485969543,64.1051912568306 -Graph NN,0.1,0,0,0,20000,CPU,447.5498752593994,70.8743155002594 -GraphTM,0.1,10000,10.0,23,10,CUDA,149.6928951740265,89.80874316939891 -TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,962.4737737178802,49.59016393442623 -Graph NN,0.2,0,0,0,20000,CPU,403.6350507736206,64.15300369262695 -GraphTM,0.2,10000,10.0,23,10,CUDA,170.02189421653748,78.16939890710383 -TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,947.2696743011475,20.116120218579233 -Graph NN,0.005,0,0,0,20000,CPU,470.0121097564697,81.20218515396118 -GraphTM,0.005,10000,10.0,23,10,CUDA,109.51706099510193,98.82513661202185 -TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,974.2360310554504,76.74180327868852 -Graph NN,0.01,0,0,0,20000,CPU,466.69573068618774,76.06557607650757 -GraphTM,0.01,10000,10.0,23,10,CUDA,112.95063591003418,98.4153005464481 -TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,948.407201051712,74.86338797814209 -Graph NN,0.02,0,0,0,20000,CPU,288.0073969364166,92.92349815368652 -GraphTM,0.02,10000,10.0,23,10,CUDA,119.34772634506226,97.48633879781421 -TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,971.228814125061,39.51502732240437 -Graph NN,0.05,0,0,0,20000,CPU,477.7228500843048,89.86338973045349 -GraphTM,0.05,10000,10.0,23,10,CUDA,135.2427453994751,94.86338797814207 -TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,964.5819866657257,34.56284153005464 -Graph NN,0.1,0,0,0,20000,CPU,459.15181946754456,71.22950553894043 -GraphTM,0.1,10000,10.0,23,10,CUDA,148.52941298484802,89.67213114754098 -TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,981.4810082912445,49.59016393442623 -Graph NN,0.2,0,0,0,20000,CPU,356.59899377822876,64.15300369262695 -GraphTM,0.2,10000,10.0,23,10,CUDA,169.7598683834076,76.85792349726775 -TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,959.9282560348511,20.116120218579233 -Graph NN,0.005,0,0,0,20000,CPU,378.94336581230164,80.32786846160889 -GraphTM,0.005,10000,10.0,23,10,CUDA,109.55144882202148,98.44262295081967 -TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,947.1284465789795,76.63934426229508 -Graph NN,0.01,0,0,0,20000,CPU,407.1111581325531,94.31694149971008 -GraphTM,0.01,10000,10.0,23,10,CUDA,112.06348276138306,98.27868852459017 -TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,941.711061000824,39.65163934426229 -Graph NN,0.02,0,0,0,20000,CPU,402.2970163822174,79.80874180793762 -GraphTM,0.02,10000,10.0,23,10,CUDA,120.20444130897522,97.8415300546448 -TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,998.2885782718658,72.1311475409836 -Graph NN,0.05,0,0,0,20000,CPU,400.97751235961914,85.30054688453674 -GraphTM,0.05,10000,10.0,23,10,CUDA,136.81029963493347,94.78142076502732 -TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,1003.2194263935089,64.1051912568306 -Graph NN,0.1,0,0,0,20000,CPU,413.25741934776306,74.59016442298889 -GraphTM,0.1,10000,10.0,23,10,CUDA,148.70455861091614,89.89071038251366 -TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,974.4099938869476,49.62431693989071 -Graph NN,0.2,0,0,0,20000,CPU,369.36416029930115,64.15300369262695 -GraphTM,0.2,10000,10.0,23,10,CUDA,170.01750564575195,78.55191256830601 -TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,990.2080008983612,20.184426229508194 -Graph NN,0.005,0,0,0,20000,CPU,440.5256702899933,90.4644787311554 -GraphTM,0.005,10000,10.0,23,10,CUDA,109.76434278488159,98.55191256830601 -TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,1004.704318523407,76.63934426229508 -Graph NN,0.01,0,0,0,20000,CPU,385.76011848449707,77.62295007705688 -GraphTM,0.01,10000,10.0,23,10,CUDA,113.28425002098083,98.44262295081967 -TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,953.8945541381836,74.93169398907104 -Graph NN,0.02,0,0,0,20000,CPU,422.2995481491089,90.71038365364075 -GraphTM,0.02,10000,10.0,23,10,CUDA,121.29091334342957,97.6775956284153 -TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,1002.099497795105,72.1311475409836 -Graph NN,0.05,0,0,0,20000,CPU,383.33483958244324,81.8306028842926 -GraphTM,0.05,10000,10.0,23,10,CUDA,134.72863698005676,94.53551912568307 -TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,936.831921339035,64.1051912568306 -Graph NN,0.1,0,0,0,20000,CPU,320.32143545150757,83.60655903816223 -GraphTM,0.1,10000,10.0,23,10,CUDA,150.56500816345215,89.15300546448087 -TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,955.8687121868134,49.62431693989071 -Graph NN,0.2,0,0,0,20000,CPU,432.34014868736267,64.15300369262695 -GraphTM,0.2,10000,10.0,23,10,CUDA,169.61127710342407,79.12568306010928 -TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,945.0617082118988,20.116120218579233 -Graph NN,0.005,0,0,0,20000,CPU,458.87039852142334,79.37158346176147 -GraphTM,0.005,10000,10.0,23,10,CUDA,110.9952290058136,98.82513661202185 -TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,985.8775904178619,76.63934426229508 -Graph NN,0.01,0,0,0,20000,CPU,453.55728340148926,76.06557607650757 -GraphTM,0.01,10000,10.0,23,10,CUDA,113.85269451141357,98.27868852459017 -TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,941.0662143230438,75.06830601092896 -Graph NN,0.02,0,0,0,20000,CPU,416.2407822608948,91.66666865348816 -GraphTM,0.02,10000,10.0,23,10,CUDA,120.69959592819214,97.78688524590164 -TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,973.9127674102783,72.40437158469946 -Graph NN,0.05,0,0,0,20000,CPU,311.2621831893921,75.46448111534119 -GraphTM,0.05,10000,10.0,23,10,CUDA,134.66055345535278,94.89071038251366 -TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,953.3380017280579,63.25136612021858 -Graph NN,0.1,0,0,0,20000,CPU,425.43416261672974,73.79781603813171 -GraphTM,0.1,10000,10.0,23,10,CUDA,150.67951107025146,90.27322404371586 -TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,967.5897221565247,49.59016393442623 -Graph NN,0.2,0,0,0,20000,CPU,379.8497235774994,64.15300369262695 -GraphTM,0.2,10000,10.0,23,10,CUDA,169.7126281261444,77.81420765027323 -TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,955.9427745342255,20.116120218579233 +Exp_id,Algorithm,Noise_Ratio,T,s,Max_Included_Literals,Epochs,Platform,Total_Time,Accuracy +20250225083536,Graph NN,0.005,0,0,0,2000,CPU,44.10563063621521,80.87431788444519 diff --git a/examples/recomm_system/experiment_results_ensamble.csv b/examples/recomm_system/experiment_results_ensamble.csv new file mode 100644 index 00000000..b394dad6 --- /dev/null +++ b/examples/recomm_system/experiment_results_ensamble.csv @@ -0,0 +1,181 @@ +Algorithm,Noise_Ratio,T,s,Max_Included_Literals,Epochs,Platform,Total_Time,Accuracy +Graph NN,0.005,0,0,0,20000,CPU,418.9250466823578,75.62841773033142 +GraphTM,0.005,10000,10.0,23,10,CUDA,110.35683226585388,98.68852459016394 +TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,1059.1634759902954,76.81010928961749 +Graph NN,0.01,0,0,0,20000,CPU,550.6980571746826,94.50819492340088 +GraphTM,0.01,10000,10.0,23,10,CUDA,114.06276345252991,98.4153005464481 +TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,1051.916612625122,75.3415300546448 +Graph NN,0.02,0,0,0,20000,CPU,475.44024682044983,75.30054450035095 +GraphTM,0.02,10000,10.0,23,10,CUDA,121.55624794960022,97.8415300546448 +TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,1043.9487817287445,72.1311475409836 +Graph NN,0.05,0,0,0,20000,CPU,411.8552327156067,80.98360896110535 +GraphTM,0.05,10000,10.0,23,10,CUDA,136.7814338207245,94.20765027322405 +TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,1044.2656917572021,64.00273224043715 +Graph NN,0.1,0,0,0,20000,CPU,484.6550889015198,68.7158465385437 +GraphTM,0.1,10000,10.0,23,10,CUDA,150.34457921981812,89.72677595628416 +TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,1061.191523551941,49.62431693989071 +Graph NN,0.2,0,0,0,20000,CPU,483.8463816642761,71.28415107727051 +GraphTM,0.2,10000,10.0,23,10,CUDA,169.18810439109802,78.49726775956285 +TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,1071.927158355713,20.116120218579233 +Graph NN,0.005,0,0,0,20000,CPU,473.5806052684784,86.36612296104431 +GraphTM,0.005,10000,10.0,23,10,CUDA,110.18979954719543,98.60655737704917 +TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,979.0509588718414,76.74180327868852 +Graph NN,0.01,0,0,0,20000,CPU,444.6897065639496,93.55190992355347 +GraphTM,0.01,10000,10.0,23,10,CUDA,112.48035550117493,98.4153005464481 +TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,1007.9654748439789,74.86338797814209 +Graph NN,0.02,0,0,0,20000,CPU,386.32835030555725,93.22404265403748 +GraphTM,0.02,10000,10.0,23,10,CUDA,120.46316766738892,97.73224043715847 +TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,1017.5866801738739,73.25819672131148 +Graph NN,0.05,0,0,0,20000,CPU,417.78410935401917,73.1693983078003 +GraphTM,0.05,10000,10.0,23,10,CUDA,135.64952206611633,95.08196721311475 +TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,945.0465729236603,64.00273224043715 +Graph NN,0.1,0,0,0,20000,CPU,481.6537721157074,77.18579173088074 +GraphTM,0.1,10000,10.0,23,10,CUDA,149.57958960533142,90.08196721311475 +TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,938.0212676525116,49.59016393442623 +Graph NN,0.2,0,0,0,20000,CPU,391.36059975624084,64.15300369262695 +GraphTM,0.2,10000,10.0,23,10,CUDA,169.49347591400146,77.65027322404372 +TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,940.9758951663971,20.116120218579233 +Graph NN,0.005,0,0,0,20000,CPU,480.5005066394806,75.68305730819702 +GraphTM,0.005,10000,10.0,23,10,CUDA,109.65927052497864,98.19672131147541 +TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,947.7581994533539,76.63934426229508 +Graph NN,0.01,0,0,0,20000,CPU,449.22584795951843,76.36612057685852 +GraphTM,0.01,10000,10.0,23,10,CUDA,113.07226181030273,98.27868852459017 +TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,1010.8711988925934,74.93169398907104 +Graph NN,0.02,0,0,0,20000,CPU,403.96647000312805,96.85792326927185 +GraphTM,0.02,10000,10.0,23,10,CUDA,120.02044725418091,97.78688524590164 +TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,1011.7896072864532,72.40437158469946 +Graph NN,0.05,0,0,0,20000,CPU,460.688773393631,85.00000238418579 +GraphTM,0.05,10000,10.0,23,10,CUDA,136.04891228675842,94.69945355191257 +TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,1014.1492829322815,64.00273224043715 +Graph NN,0.1,0,0,0,20000,CPU,407.9346880912781,74.1256833076477 +GraphTM,0.1,10000,10.0,23,10,CUDA,149.6586093902588,90.08196721311475 +TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,990.8282098770142,49.59016393442623 +Graph NN,0.2,0,0,0,20000,CPU,437.8108870983124,65.60109257698059 +GraphTM,0.2,10000,10.0,23,10,CUDA,168.44772601127625,78.93442622950819 +TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,1022.1848647594452,20.116120218579233 +Graph NN,0.005,0,0,0,20000,CPU,430.3925087451935,89.20764923095703 +GraphTM,0.005,10000,10.0,23,10,CUDA,109.6658935546875,98.68852459016394 +TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,1016.199923992157,76.63934426229508 +Graph NN,0.01,0,0,0,20000,CPU,396.3338620662689,84.23497080802917 +GraphTM,0.01,10000,10.0,23,10,CUDA,113.67849016189575,98.27868852459017 +TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,944.4602844715118,74.86338797814209 +Graph NN,0.02,0,0,0,20000,CPU,434.91951632499695,93.25136542320251 +GraphTM,0.02,10000,10.0,23,10,CUDA,120.31921482086182,97.8415300546448 +TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,933.2245874404907,72.40437158469946 +Graph NN,0.05,0,0,0,20000,CPU,483.2671537399292,80.32786846160889 +GraphTM,0.05,10000,10.0,23,10,CUDA,135.68922591209412,94.78142076502732 +TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,994.6384744644165,64.13934426229508 +Graph NN,0.1,0,0,0,20000,CPU,424.9935986995697,81.33879899978638 +GraphTM,0.1,10000,10.0,23,10,CUDA,149.08107113838196,89.59016393442623 +TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,944.0273253917694,49.62431693989071 +Graph NN,0.2,0,0,0,20000,CPU,333.49274706840515,61.50273084640503 +GraphTM,0.2,10000,10.0,23,10,CUDA,170.906751871109,78.98907103825137 +TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,965.9725024700165,20.116120218579233 +Graph NN,0.005,0,0,0,20000,CPU,377.28471970558167,75.68305730819702 +GraphTM,0.005,10000,10.0,23,10,CUDA,109.61631536483765,98.82513661202185 +TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,976.8008840084076,76.67349726775956 +Graph NN,0.01,0,0,0,20000,CPU,473.2922372817993,76.06557607650757 +GraphTM,0.01,10000,10.0,23,10,CUDA,112.87212014198303,98.27868852459017 +TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,942.7254059314728,74.86338797814209 +Graph NN,0.02,0,0,0,20000,CPU,357.36573815345764,75.40983557701111 +GraphTM,0.02,10000,10.0,23,10,CUDA,119.41612005233765,97.8415300546448 +TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,985.81947016716,72.40437158469946 +Graph NN,0.05,0,0,0,20000,CPU,440.75843334198,73.08743000030518 +GraphTM,0.05,10000,10.0,23,10,CUDA,136.8215868473053,94.91803278688525 +TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,997.739678144455,64.1051912568306 +Graph NN,0.1,0,0,0,20000,CPU,426.73446226119995,88.55191469192505 +GraphTM,0.1,10000,10.0,23,10,CUDA,149.54467248916626,89.94535519125682 +TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,980.096907377243,49.59016393442623 +Graph NN,0.2,0,0,0,20000,CPU,387.20843958854675,75.71038007736206 +GraphTM,0.2,10000,10.0,23,10,CUDA,169.7962884902954,77.56830601092896 +TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,987.0616261959076,20.116120218579233 +Graph NN,0.005,0,0,0,20000,CPU,455.586905002594,83.41529965400696 +GraphTM,0.005,10000,10.0,23,10,CUDA,109.7424705028534,98.5792349726776 +TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,998.4698519706726,76.81010928961749 +Graph NN,0.01,0,0,0,20000,CPU,466.44022035598755,98.52458834648132 +GraphTM,0.01,10000,10.0,23,10,CUDA,112.78495740890503,98.27868852459017 +TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,932.3163437843323,74.93169398907104 +Graph NN,0.02,0,0,0,20000,CPU,455.35024762153625,88.96175026893616 +GraphTM,0.02,10000,10.0,23,10,CUDA,120.741384267807,97.75956284153006 +TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,974.3740100860596,72.1311475409836 +Graph NN,0.05,0,0,0,20000,CPU,399.9565739631653,73.60655665397644 +GraphTM,0.05,10000,10.0,23,10,CUDA,136.17181992530823,94.67213114754098 +TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,971.1499485969543,64.1051912568306 +Graph NN,0.1,0,0,0,20000,CPU,447.5498752593994,70.8743155002594 +GraphTM,0.1,10000,10.0,23,10,CUDA,149.6928951740265,89.80874316939891 +TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,962.4737737178802,49.59016393442623 +Graph NN,0.2,0,0,0,20000,CPU,403.6350507736206,64.15300369262695 +GraphTM,0.2,10000,10.0,23,10,CUDA,170.02189421653748,78.16939890710383 +TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,947.2696743011475,20.116120218579233 +Graph NN,0.005,0,0,0,20000,CPU,470.0121097564697,81.20218515396118 +GraphTM,0.005,10000,10.0,23,10,CUDA,109.51706099510193,98.82513661202185 +TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,974.2360310554504,76.74180327868852 +Graph NN,0.01,0,0,0,20000,CPU,466.69573068618774,76.06557607650757 +GraphTM,0.01,10000,10.0,23,10,CUDA,112.95063591003418,98.4153005464481 +TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,948.407201051712,74.86338797814209 +Graph NN,0.02,0,0,0,20000,CPU,288.0073969364166,92.92349815368652 +GraphTM,0.02,10000,10.0,23,10,CUDA,119.34772634506226,97.48633879781421 +TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,971.228814125061,39.51502732240437 +Graph NN,0.05,0,0,0,20000,CPU,477.7228500843048,89.86338973045349 +GraphTM,0.05,10000,10.0,23,10,CUDA,135.2427453994751,94.86338797814207 +TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,964.5819866657257,34.56284153005464 +Graph NN,0.1,0,0,0,20000,CPU,459.15181946754456,71.22950553894043 +GraphTM,0.1,10000,10.0,23,10,CUDA,148.52941298484802,89.67213114754098 +TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,981.4810082912445,49.59016393442623 +Graph NN,0.2,0,0,0,20000,CPU,356.59899377822876,64.15300369262695 +GraphTM,0.2,10000,10.0,23,10,CUDA,169.7598683834076,76.85792349726775 +TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,959.9282560348511,20.116120218579233 +Graph NN,0.005,0,0,0,20000,CPU,378.94336581230164,80.32786846160889 +GraphTM,0.005,10000,10.0,23,10,CUDA,109.55144882202148,98.44262295081967 +TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,947.1284465789795,76.63934426229508 +Graph NN,0.01,0,0,0,20000,CPU,407.1111581325531,94.31694149971008 +GraphTM,0.01,10000,10.0,23,10,CUDA,112.06348276138306,98.27868852459017 +TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,941.711061000824,39.65163934426229 +Graph NN,0.02,0,0,0,20000,CPU,402.2970163822174,79.80874180793762 +GraphTM,0.02,10000,10.0,23,10,CUDA,120.20444130897522,97.8415300546448 +TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,998.2885782718658,72.1311475409836 +Graph NN,0.05,0,0,0,20000,CPU,400.97751235961914,85.30054688453674 +GraphTM,0.05,10000,10.0,23,10,CUDA,136.81029963493347,94.78142076502732 +TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,1003.2194263935089,64.1051912568306 +Graph NN,0.1,0,0,0,20000,CPU,413.25741934776306,74.59016442298889 +GraphTM,0.1,10000,10.0,23,10,CUDA,148.70455861091614,89.89071038251366 +TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,974.4099938869476,49.62431693989071 +Graph NN,0.2,0,0,0,20000,CPU,369.36416029930115,64.15300369262695 +GraphTM,0.2,10000,10.0,23,10,CUDA,170.01750564575195,78.55191256830601 +TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,990.2080008983612,20.184426229508194 +Graph NN,0.005,0,0,0,20000,CPU,440.5256702899933,90.4644787311554 +GraphTM,0.005,10000,10.0,23,10,CUDA,109.76434278488159,98.55191256830601 +TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,1004.704318523407,76.63934426229508 +Graph NN,0.01,0,0,0,20000,CPU,385.76011848449707,77.62295007705688 +GraphTM,0.01,10000,10.0,23,10,CUDA,113.28425002098083,98.44262295081967 +TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,953.8945541381836,74.93169398907104 +Graph NN,0.02,0,0,0,20000,CPU,422.2995481491089,90.71038365364075 +GraphTM,0.02,10000,10.0,23,10,CUDA,121.29091334342957,97.6775956284153 +TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,1002.099497795105,72.1311475409836 +Graph NN,0.05,0,0,0,20000,CPU,383.33483958244324,81.8306028842926 +GraphTM,0.05,10000,10.0,23,10,CUDA,134.72863698005676,94.53551912568307 +TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,936.831921339035,64.1051912568306 +Graph NN,0.1,0,0,0,20000,CPU,320.32143545150757,83.60655903816223 +GraphTM,0.1,10000,10.0,23,10,CUDA,150.56500816345215,89.15300546448087 +TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,955.8687121868134,49.62431693989071 +Graph NN,0.2,0,0,0,20000,CPU,432.34014868736267,64.15300369262695 +GraphTM,0.2,10000,10.0,23,10,CUDA,169.61127710342407,79.12568306010928 +TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,945.0617082118988,20.116120218579233 +Graph NN,0.005,0,0,0,20000,CPU,458.87039852142334,79.37158346176147 +GraphTM,0.005,10000,10.0,23,10,CUDA,110.9952290058136,98.82513661202185 +TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,985.8775904178619,76.63934426229508 +Graph NN,0.01,0,0,0,20000,CPU,453.55728340148926,76.06557607650757 +GraphTM,0.01,10000,10.0,23,10,CUDA,113.85269451141357,98.27868852459017 +TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,941.0662143230438,75.06830601092896 +Graph NN,0.02,0,0,0,20000,CPU,416.2407822608948,91.66666865348816 +GraphTM,0.02,10000,10.0,23,10,CUDA,120.69959592819214,97.78688524590164 +TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,973.9127674102783,72.40437158469946 +Graph NN,0.05,0,0,0,20000,CPU,311.2621831893921,75.46448111534119 +GraphTM,0.05,10000,10.0,23,10,CUDA,134.66055345535278,94.89071038251366 +TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,953.3380017280579,63.25136612021858 +Graph NN,0.1,0,0,0,20000,CPU,425.43416261672974,73.79781603813171 +GraphTM,0.1,10000,10.0,23,10,CUDA,150.67951107025146,90.27322404371586 +TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,967.5897221565247,49.59016393442623 +Graph NN,0.2,0,0,0,20000,CPU,379.8497235774994,64.15300369262695 +GraphTM,0.2,10000,10.0,23,10,CUDA,169.7126281261444,77.81420765027323 +TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,955.9427745342255,20.116120218579233 diff --git a/examples/recomm_system/graph_nn.py b/examples/recomm_system/graph_nn.py index 9ef5fbed..ef6922a7 100644 --- a/examples/recomm_system/graph_nn.py +++ b/examples/recomm_system/graph_nn.py @@ -94,6 +94,7 @@ def forward(self, x, edge_index): total_time = benchmark_total.elapsed() # Append results for each epoch results.append({ + "Exp_id": args.exp_id, "Algorithm": "Graph NN", "Noise_Ratio": args.dataset_noise_ratio, "T": 0, @@ -118,8 +119,9 @@ def forward(self, x, edge_index): def default_args(**kwargs): parser = argparse.ArgumentParser() parser.add_argument("--platform", default="CPU", type=str, choices=["CPU", "CUDA"]) - parser.add_argument("--epochs", default=20000, type=int) + parser.add_argument("--epochs", default=2000, type=int) parser.add_argument("--dataset_noise_ratio", default=0.01, type=float) + parser.add_argument("--exp_id", default="", type=str) args = parser.parse_args() for key, value in kwargs.items(): if key in args.__dict__: diff --git a/examples/recomm_system/graph_tm.py b/examples/recomm_system/graph_tm.py index d03d3be9..27c30828 100644 --- a/examples/recomm_system/graph_tm.py +++ b/examples/recomm_system/graph_tm.py @@ -127,6 +127,7 @@ def main(args): total_time = benchmark_total.elapsed() # result_train = 100*(tm.predict(graphs_train) == Y_train).mean() results.append({ + "Exp_id": args.exp_id, "Algorithm": "GraphTM", "Noise_Ratio": args.dataset_noise_ratio, "T": args.T, @@ -163,6 +164,7 @@ def default_args(**kwargs): parser.add_argument("--noise", default=0.01, type=float) parser.add_argument("--max-included-literals", default=23, type=int) parser.add_argument("--dataset_noise_ratio", default=0.01, type=float) + parser.add_argument("--exp_id", default="", type=str) args = parser.parse_args() for key, value in kwargs.items(): if key in args.__dict__: diff --git a/examples/recomm_system/main.sh b/examples/recomm_system/main.sh index a5db0425..82b03778 100644 --- a/examples/recomm_system/main.sh +++ b/examples/recomm_system/main.sh @@ -6,6 +6,9 @@ set -e # exit if error models="graph_tm tm_classifier graph_nn" dataset_noise_ratios="0.005 0.01 0.02 0.05 0.1 0.2" num_iterations=10 # Number of times to repeat the experiments +exp_id=$(date +%Y%m%d%H%M%S) + +echo 'Experiment ID: ' $exp_id for (( i=1; i<=num_iterations; i++ )) do @@ -13,13 +16,13 @@ do for N in $dataset_noise_ratios; do echo `date`, Running Graph NN ... - python3 graph_nn.py --dataset_noise_ratio $N + python3 graph_nn.py --dataset_noise_ratio $N --exp_id $exp_id echo `date`, Running Graph Tsetlin Machine ... - python3 graph_tm.py --dataset_noise_ratio $N + python3 graph_tm.py --dataset_noise_ratio $N --exp_id $exp_id echo `date`, Running Tsetlin Machine Classifier ... - python3 tm_classifier.py --dataset_noise_ratio $N + python3 tm_classifier.py --dataset_noise_ratio $N --exp_id $exp_id done done diff --git a/examples/recomm_system/test.ipynb b/examples/recomm_system/test.ipynb index 4e44624f..9873a435 100644 --- a/examples/recomm_system/test.ipynb +++ b/examples/recomm_system/test.ipynb @@ -67,40 +67,13 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - " Algorithm Noise_Ratio T s Max_Included_Literals Epochs \\\n", - "0 Graph NN 0.005 0 0.0 0 20000 \n", - "1 GraphTM 0.005 10000 10.0 23 10 \n", - "2 TMClassifier 0.005 10000 10.0 32 10 \n", - "3 Graph NN 0.010 0 0.0 0 20000 \n", - "4 GraphTM 0.010 10000 10.0 23 10 \n", - ".. ... ... ... ... ... ... \n", - "175 GraphTM 0.100 10000 10.0 23 10 \n", - "176 TMClassifier 0.100 10000 10.0 32 10 \n", - "177 Graph NN 0.200 0 0.0 0 20000 \n", - "178 GraphTM 0.200 10000 10.0 23 10 \n", - "179 TMClassifier 0.200 10000 10.0 32 10 \n", - "\n", - " Platform Total_Time Accuracy \n", - "0 CPU 418.925047 75.628418 \n", - "1 CUDA 110.356832 98.688525 \n", - "2 CPU_sparse 1059.163476 76.810109 \n", - "3 CPU 550.698057 94.508195 \n", - "4 CUDA 114.062763 98.415301 \n", - ".. ... ... ... \n", - "175 CUDA 150.679511 90.273224 \n", - "176 CPU_sparse 967.589722 49.590164 \n", - "177 CPU 379.849724 64.153004 \n", - "178 CUDA 169.712628 77.814208 \n", - "179 CPU_sparse 955.942775 20.116120 \n", - "\n", - "[180 rows x 9 columns]\n", "\n", "\\begin{table}[h!]\n", "\\centering\n", @@ -128,7 +101,7 @@ "# This assumes each algorithm data spans three consecutive rows\n", "start_index = 0\n", "range_records = data.iloc[start_index:len(data)]\n", - "print(range_records)\n", + "# print(range_records)\n", "# Create a dictionary to store the accuracy values\n", "noise_accuracies = {}\n", "\n", diff --git a/examples/recomm_system/tm_classifier.py b/examples/recomm_system/tm_classifier.py index 876f8c4f..cb6cb458 100644 --- a/examples/recomm_system/tm_classifier.py +++ b/examples/recomm_system/tm_classifier.py @@ -34,6 +34,7 @@ def main(args): # Append results for each epoch results.append({ + "Exp_id": args.exp_id, "Algorithm": "TMClassifier", "Noise_Ratio": args.dataset_noise_ratio, "T": args.T, @@ -64,6 +65,7 @@ def default_args(**kwargs): parser.add_argument("--weighted_clauses", default=True, type=bool) parser.add_argument("--epochs", default=10, type=int) parser.add_argument("--dataset_noise_ratio", default=0.01, type=float) + parser.add_argument("--exp_id", default="", type=str) args = parser.parse_args() for key, value in kwargs.items(): if key in args.__dict__: From f057bbe0829815da8fc7a8edff522031161a1272 Mon Sep 17 00:00:00 2001 From: Ahmed Khalid Date: Fri, 28 Feb 2025 07:39:30 +0000 Subject: [PATCH 25/33] update --- examples/recomm_system/experiment_results.csv | 94 +++++++++++++++++++ examples/recomm_system/test.ipynb | 25 +++-- 2 files changed, 106 insertions(+), 13 deletions(-) diff --git a/examples/recomm_system/experiment_results.csv b/examples/recomm_system/experiment_results.csv index 09e4f8e7..a2902234 100644 --- a/examples/recomm_system/experiment_results.csv +++ b/examples/recomm_system/experiment_results.csv @@ -1,2 +1,96 @@ Exp_id,Algorithm,Noise_Ratio,T,s,Max_Included_Literals,Epochs,Platform,Total_Time,Accuracy 20250225083536,Graph NN,0.005,0,0,0,2000,CPU,44.10563063621521,80.87431788444519 +20250225090119,Graph NN,0.005,0,0,0,2000,CPU,49.34887194633484,84.45355296134949 +20250225090119,GraphTM,0.005,10000,10.0,23,10,CUDA,109.94287848472595,98.82513661202185 +20250225090119,TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,944.8344459533691,76.63934426229508 +20250225090119,Graph NN,0.01,0,0,0,2000,CPU,36.30448269844055,81.99453353881836 +20250225090119,GraphTM,0.01,10000,10.0,23,10,CUDA,113.23237609863281,98.4153005464481 +20250225090119,TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,936.9574060440063,74.93169398907104 +20250225090119,Graph NN,0.02,0,0,0,2000,CPU,38.477863073349,87.54098415374756 +20250225090119,GraphTM,0.02,10000,10.0,23,10,CUDA,120.61202812194824,97.73224043715847 +20250225090119,TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,1001.9182693958282,72.1311475409836 +20250225090119,Graph NN,0.05,0,0,0,2000,CPU,48.03118896484375,79.20765280723572 +20250225090119,GraphTM,0.05,10000,10.0,23,10,CUDA,135.30033922195435,95.10928961748634 +20250225090119,TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,962.4823818206787,64.17349726775956 +20250225090119,Graph NN,0.1,0,0,0,2000,CPU,35.70058226585388,73.55191111564636 +20250225090119,GraphTM,0.1,10000,10.0,23,10,CUDA,150.0443034172058,90.19125683060109 +20250225090119,TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,936.1979103088379,49.35109289617486 +20250225090119,Graph NN,0.2,0,0,0,2000,CPU,40.174824714660645,63.22404146194458 +20250225090119,GraphTM,0.2,10000,10.0,23,10,CUDA,169.9642357826233,77.95081967213115 +20250225090119,TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,959.6620714664459,20.116120218579233 +20250225090119,Graph NN,0.005,0,0,0,2000,CPU,35.725218534469604,89.75409865379333 +20250225090119,GraphTM,0.005,10000,10.0,23,10,CUDA,110.31502270698547,98.60655737704917 +20250225090119,TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,1024.4315028190613,76.74180327868852 +20250225090119,Graph NN,0.01,0,0,0,2000,CPU,32.86650729179382,83.03278684616089 +20250225090119,GraphTM,0.01,10000,10.0,23,10,CUDA,112.99009418487549,98.4153005464481 +20250225090119,TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,1040.9654848575592,73.49726775956285 +20250225090119,Graph NN,0.02,0,0,0,2000,CPU,29.535728454589844,88.63387703895569 +20250225090119,GraphTM,0.02,10000,10.0,23,10,CUDA,120.96075296401978,97.78688524590164 +20250225090119,TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,1054.357099056244,72.78005464480874 +20250225090119,Graph NN,0.05,0,0,0,2000,CPU,47.468485832214355,75.10929107666016 +20250225090119,GraphTM,0.05,10000,10.0,23,10,CUDA,134.4755368232727,95.08196721311475 +20250225090119,TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,949.9652540683746,63.79781420765027 +20250225090119,Graph NN,0.1,0,0,0,2000,CPU,38.58360719680786,71.967214345932 +20250225090119,GraphTM,0.1,10000,10.0,23,10,CUDA,148.94670748710632,90.1639344262295 +20250225090119,TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,1027.843326807022,49.14617486338798 +20250225090119,Graph NN,0.2,0,0,0,2000,CPU,37.01042413711548,66.42076373100281 +20250225090119,GraphTM,0.2,10000,10.0,23,10,CUDA,169.78875064849854,79.94535519125682 +20250225090119,TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,1019.9154229164124,20.116120218579233 +20250225090119,Graph NN,0.005,0,0,0,2000,CPU,46.6854362487793,75.84699392318726 +20250225090119,GraphTM,0.005,10000,10.0,23,10,CUDA,110.07307553291321,98.82513661202185 +20250225090119,TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,1019.9013290405273,76.74180327868852 +20250225090119,Graph NN,0.01,0,0,0,2000,CPU,41.087942600250244,83.77048969268799 +20250225090119,GraphTM,0.01,10000,10.0,23,10,CUDA,112.69408297538757,98.4153005464481 +20250225090119,TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,1012.4928967952728,75.10245901639344 +20250225090119,Graph NN,0.02,0,0,0,2000,CPU,42.92523193359375,88.44262361526489 +20250225090119,GraphTM,0.02,10000,10.0,23,10,CUDA,120.11772298812866,97.59562841530055 +20250225090119,TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,1006.3243997097015,72.1311475409836 +20250225090119,Graph NN,0.05,0,0,0,2000,CPU,36.374452352523804,85.49180030822754 +20250225090119,GraphTM,0.05,10000,10.0,23,10,CUDA,134.70963144302368,94.15300546448087 +20250225090119,TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,959.455883026123,64.00273224043715 +20250225090119,Graph NN,0.1,0,0,0,2000,CPU,39.68649101257324,78.68852615356445 +20250225090119,GraphTM,0.1,10000,10.0,23,10,CUDA,151.08690643310547,89.31693989071037 +20250225090119,TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,1008.2197906970978,49.24863387978142 +20250225090119,Graph NN,0.2,0,0,0,2000,CPU,36.5257625579834,67.73223876953125 +20250225090119,GraphTM,0.2,10000,10.0,23,10,CUDA,170.00959873199463,76.66666666666667 +20250225090119,TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,974.8701865673065,20.116120218579233 +20250225090119,Graph NN,0.005,0,0,0,2000,CPU,42.808833599090576,87.62295246124268 +20250225090119,GraphTM,0.005,10000,10.0,23,10,CUDA,110.3779969215393,98.63387978142076 +20250225090119,TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,959.8772912025452,76.63934426229508 +20250225090119,Graph NN,0.01,0,0,0,2000,CPU,42.59048676490784,82.07650184631348 +20250225090119,GraphTM,0.01,10000,10.0,23,10,CUDA,113.87734937667847,98.4153005464481 +20250225090119,TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,990.1339011192322,74.93169398907104 +20250225090119,Graph NN,0.02,0,0,0,2000,CPU,35.95067048072815,79.64481115341187 +20250225090119,GraphTM,0.02,10000,10.0,23,10,CUDA,120.58509016036987,97.8415300546448 +20250225090119,TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,976.5062215328217,71.9603825136612 +20250225090119,Graph NN,0.05,0,0,0,2000,CPU,49.44124245643616,76.09289884567261 +20250225090119,GraphTM,0.05,10000,10.0,23,10,CUDA,136.56197214126587,94.89071038251366 +20250225090119,TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,971.7774770259857,63.25136612021858 +20250225090119,Graph NN,0.1,0,0,0,2000,CPU,45.797210931777954,73.5792338848114 +20250225090119,GraphTM,0.1,10000,10.0,23,10,CUDA,149.67395901679993,89.23497267759562 +20250225090119,TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,979.9483859539032,49.59016393442623 +20250225090119,Graph NN,0.2,0,0,0,2000,CPU,41.42583513259888,68.27868819236755 +20250225090119,GraphTM,0.2,10000,10.0,23,10,CUDA,170.87367057800293,79.18032786885246 +20250225090119,TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,961.5906836986542,20.081967213114755 +20250225090119,Graph NN,0.005,0,0,0,2000,CPU,43.93612337112427,79.20765280723572 +20250225090119,GraphTM,0.005,10000,10.0,23,10,CUDA,109.73634815216064,98.63387978142076 +20250225090119,TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,965.0153555870056,76.63934426229508 +20250225090119,Graph NN,0.01,0,0,0,2000,CPU,49.67618227005005,92.45901703834534 +20250225090119,GraphTM,0.01,10000,10.0,23,10,CUDA,113.5588014125824,98.4153005464481 +20250225090119,TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,983.9533984661102,74.4535519125683 +20250225090119,Graph NN,0.02,0,0,0,2000,CPU,36.16115427017212,80.87431788444519 +20250225090119,GraphTM,0.02,10000,10.0,23,10,CUDA,120.58146834373474,97.81420765027322 +20250225090119,TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,944.9977910518646,71.51639344262296 +20250225090119,Graph NN,0.05,0,0,0,2000,CPU,48.164318561553955,83.77048969268799 +20250225090119,GraphTM,0.05,10000,10.0,23,10,CUDA,135.97020173072815,94.75409836065573 +20250225090119,TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,937.3424286842346,64.1051912568306 +20250225090119,Graph NN,0.1,0,0,0,2000,CPU,46.862754344940186,70.84699273109436 +20250225090119,GraphTM,0.1,10000,10.0,23,10,CUDA,149.9700825214386,89.72677595628416 +20250225090119,TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,990.8207032680511,49.86338797814208 +20250225090119,Graph NN,0.2,0,0,0,2000,CPU,48.0979220867157,63.66119980812073 +20250225090119,GraphTM,0.2,10000,10.0,23,10,CUDA,170.79332089424133,78.63387978142077 +20250225090119,TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,990.5863327980042,20.116120218579233 +20250225090119,Graph NN,0.005,0,0,0,2000,CPU,39.39827084541321,83.2513689994812 +20250225090119,GraphTM,0.005,10000,10.0,23,10,CUDA,109.96842241287231,98.52459016393442 +20250225090119,TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,941.0268743038177,76.63934426229508 +20250225090119,Graph NN,0.01,0,0,0,2000,CPU,37.69904541969299,93.44262480735779 diff --git a/examples/recomm_system/test.ipynb b/examples/recomm_system/test.ipynb index 9873a435..c5c06961 100644 --- a/examples/recomm_system/test.ipynb +++ b/examples/recomm_system/test.ipynb @@ -67,7 +67,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 10, "metadata": {}, "outputs": [ { @@ -80,12 +80,12 @@ "\\begin{tabular}{|c|c|c|c|}\n", "\\hline\n", "\\textbf{Noise Ratio} & \\textbf{GCN (\\%)} & \\textbf{GTM (\\%)} & \\textbf{TMClassifier (\\%)} \\\\ \\hline\n", - "0.005 & 81.73 & 98.62 & 76.70 \\\\ \\hline\n", - "0.01 & 84.73 & 98.34 & 71.43 \\\\ \\hline\n", - "0.02 & 87.81 & 97.76 & 69.09 \\\\ \\hline\n", - "0.05 & 79.86 & 94.74 & 61.04 \\\\ \\hline\n", - "0.1 & 76.40 & 89.82 & 49.60 \\\\ \\hline\n", - "0.2 & 65.90 & 78.22 & 20.12 \\\\ \\hline\n", + "0.005 & 83.36 & 98.67 & 76.67 \\\\ \\hline\n", + "0.01 & 86.13 & 98.42 & 74.58 \\\\ \\hline\n", + "0.02 & 85.03 & 97.75 & 72.10 \\\\ \\hline\n", + "0.05 & 79.93 & 94.80 & 63.87 \\\\ \\hline\n", + "0.1 & 73.73 & 89.73 & 49.44 \\\\ \\hline\n", + "0.2 & 65.86 & 78.48 & 20.11 \\\\ \\hline\n", "\\end{tabular}\n", "\\caption{Average accuracy comparison of GCN, GraphTM, and TMClassifier for varying noise ratios.}\n", "\\label{tab:recomm_sys_accuracy}\n", @@ -96,18 +96,17 @@ "source": [ "import pandas as pd\n", "data = pd.read_csv(\"experiment_results.csv\")\n", + "exp_id = \"20250225090119\" \n", + "data['Exp_id'] = data['Exp_id'].astype(str)\n", + "filtered_data = data[data['Exp_id'] == exp_id]\n", + "# print(filtered_data)\n", "\n", - "# Extract records within the specified range, e.g., rows 3 to 5 (0-indexed)\n", - "# This assumes each algorithm data spans three consecutive rows\n", - "start_index = 0\n", - "range_records = data.iloc[start_index:len(data)]\n", - "# print(range_records)\n", "# Create a dictionary to store the accuracy values\n", "noise_accuracies = {}\n", "\n", "# Algorithm,Noise_Ratio,T,s,Max_Included_Literals,Epochs,Platform,Total_Time,Accuracy\n", "# Group the data by Algorithm and Noise Ratio to calculate average accuracies\n", - "grouped_data = data.groupby(['Algorithm', 'Noise_Ratio']).agg({'Accuracy': 'mean'}).reset_index()\n", + "grouped_data = filtered_data.groupby(['Algorithm', 'Noise_Ratio']).agg({'Accuracy': 'mean'}).reset_index()\n", "\n", "# Pivot the data to get a structure suitable for LaTeX table generation\n", "pivot_data = grouped_data.pivot(index='Noise_Ratio', columns='Algorithm', values='Accuracy')\n", From de8eb1b65a68af29572ac6c9c370207845ab9fc1 Mon Sep 17 00:00:00 2001 From: Ahmed Khalid Date: Fri, 28 Feb 2025 07:40:11 +0000 Subject: [PATCH 26/33] update --- examples/recomm_system/test.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/recomm_system/test.ipynb b/examples/recomm_system/test.ipynb index c5c06961..07b30e69 100644 --- a/examples/recomm_system/test.ipynb +++ b/examples/recomm_system/test.ipynb @@ -67,7 +67,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 1, "metadata": {}, "outputs": [ { From bfdf40c91faef3868a184e64813dfbb5da077487 Mon Sep 17 00:00:00 2001 From: Ahmed Khalid Date: Thu, 10 Apr 2025 10:19:34 +0000 Subject: [PATCH 27/33] rerun --- examples/recomm_system/experiment_results.csv | 275 ++++++++++++------ .../experiment_results_ensamble.csv | 181 ------------ .../recomm_system/experiment_results_old.csv | 271 ----------------- examples/recomm_system/test.ipynb | 16 +- 4 files changed, 188 insertions(+), 555 deletions(-) delete mode 100644 examples/recomm_system/experiment_results_ensamble.csv delete mode 100644 examples/recomm_system/experiment_results_old.csv diff --git a/examples/recomm_system/experiment_results.csv b/examples/recomm_system/experiment_results.csv index a2902234..45da5c4e 100644 --- a/examples/recomm_system/experiment_results.csv +++ b/examples/recomm_system/experiment_results.csv @@ -1,96 +1,181 @@ Exp_id,Algorithm,Noise_Ratio,T,s,Max_Included_Literals,Epochs,Platform,Total_Time,Accuracy -20250225083536,Graph NN,0.005,0,0,0,2000,CPU,44.10563063621521,80.87431788444519 -20250225090119,Graph NN,0.005,0,0,0,2000,CPU,49.34887194633484,84.45355296134949 -20250225090119,GraphTM,0.005,10000,10.0,23,10,CUDA,109.94287848472595,98.82513661202185 -20250225090119,TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,944.8344459533691,76.63934426229508 -20250225090119,Graph NN,0.01,0,0,0,2000,CPU,36.30448269844055,81.99453353881836 -20250225090119,GraphTM,0.01,10000,10.0,23,10,CUDA,113.23237609863281,98.4153005464481 -20250225090119,TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,936.9574060440063,74.93169398907104 -20250225090119,Graph NN,0.02,0,0,0,2000,CPU,38.477863073349,87.54098415374756 -20250225090119,GraphTM,0.02,10000,10.0,23,10,CUDA,120.61202812194824,97.73224043715847 -20250225090119,TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,1001.9182693958282,72.1311475409836 -20250225090119,Graph NN,0.05,0,0,0,2000,CPU,48.03118896484375,79.20765280723572 -20250225090119,GraphTM,0.05,10000,10.0,23,10,CUDA,135.30033922195435,95.10928961748634 -20250225090119,TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,962.4823818206787,64.17349726775956 -20250225090119,Graph NN,0.1,0,0,0,2000,CPU,35.70058226585388,73.55191111564636 -20250225090119,GraphTM,0.1,10000,10.0,23,10,CUDA,150.0443034172058,90.19125683060109 -20250225090119,TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,936.1979103088379,49.35109289617486 -20250225090119,Graph NN,0.2,0,0,0,2000,CPU,40.174824714660645,63.22404146194458 -20250225090119,GraphTM,0.2,10000,10.0,23,10,CUDA,169.9642357826233,77.95081967213115 -20250225090119,TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,959.6620714664459,20.116120218579233 -20250225090119,Graph NN,0.005,0,0,0,2000,CPU,35.725218534469604,89.75409865379333 -20250225090119,GraphTM,0.005,10000,10.0,23,10,CUDA,110.31502270698547,98.60655737704917 -20250225090119,TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,1024.4315028190613,76.74180327868852 -20250225090119,Graph NN,0.01,0,0,0,2000,CPU,32.86650729179382,83.03278684616089 -20250225090119,GraphTM,0.01,10000,10.0,23,10,CUDA,112.99009418487549,98.4153005464481 -20250225090119,TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,1040.9654848575592,73.49726775956285 -20250225090119,Graph NN,0.02,0,0,0,2000,CPU,29.535728454589844,88.63387703895569 -20250225090119,GraphTM,0.02,10000,10.0,23,10,CUDA,120.96075296401978,97.78688524590164 -20250225090119,TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,1054.357099056244,72.78005464480874 -20250225090119,Graph NN,0.05,0,0,0,2000,CPU,47.468485832214355,75.10929107666016 -20250225090119,GraphTM,0.05,10000,10.0,23,10,CUDA,134.4755368232727,95.08196721311475 -20250225090119,TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,949.9652540683746,63.79781420765027 -20250225090119,Graph NN,0.1,0,0,0,2000,CPU,38.58360719680786,71.967214345932 -20250225090119,GraphTM,0.1,10000,10.0,23,10,CUDA,148.94670748710632,90.1639344262295 -20250225090119,TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,1027.843326807022,49.14617486338798 -20250225090119,Graph NN,0.2,0,0,0,2000,CPU,37.01042413711548,66.42076373100281 -20250225090119,GraphTM,0.2,10000,10.0,23,10,CUDA,169.78875064849854,79.94535519125682 -20250225090119,TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,1019.9154229164124,20.116120218579233 -20250225090119,Graph NN,0.005,0,0,0,2000,CPU,46.6854362487793,75.84699392318726 -20250225090119,GraphTM,0.005,10000,10.0,23,10,CUDA,110.07307553291321,98.82513661202185 -20250225090119,TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,1019.9013290405273,76.74180327868852 -20250225090119,Graph NN,0.01,0,0,0,2000,CPU,41.087942600250244,83.77048969268799 -20250225090119,GraphTM,0.01,10000,10.0,23,10,CUDA,112.69408297538757,98.4153005464481 -20250225090119,TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,1012.4928967952728,75.10245901639344 -20250225090119,Graph NN,0.02,0,0,0,2000,CPU,42.92523193359375,88.44262361526489 -20250225090119,GraphTM,0.02,10000,10.0,23,10,CUDA,120.11772298812866,97.59562841530055 -20250225090119,TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,1006.3243997097015,72.1311475409836 -20250225090119,Graph NN,0.05,0,0,0,2000,CPU,36.374452352523804,85.49180030822754 -20250225090119,GraphTM,0.05,10000,10.0,23,10,CUDA,134.70963144302368,94.15300546448087 -20250225090119,TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,959.455883026123,64.00273224043715 -20250225090119,Graph NN,0.1,0,0,0,2000,CPU,39.68649101257324,78.68852615356445 -20250225090119,GraphTM,0.1,10000,10.0,23,10,CUDA,151.08690643310547,89.31693989071037 -20250225090119,TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,1008.2197906970978,49.24863387978142 -20250225090119,Graph NN,0.2,0,0,0,2000,CPU,36.5257625579834,67.73223876953125 -20250225090119,GraphTM,0.2,10000,10.0,23,10,CUDA,170.00959873199463,76.66666666666667 -20250225090119,TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,974.8701865673065,20.116120218579233 -20250225090119,Graph NN,0.005,0,0,0,2000,CPU,42.808833599090576,87.62295246124268 -20250225090119,GraphTM,0.005,10000,10.0,23,10,CUDA,110.3779969215393,98.63387978142076 -20250225090119,TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,959.8772912025452,76.63934426229508 -20250225090119,Graph NN,0.01,0,0,0,2000,CPU,42.59048676490784,82.07650184631348 -20250225090119,GraphTM,0.01,10000,10.0,23,10,CUDA,113.87734937667847,98.4153005464481 -20250225090119,TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,990.1339011192322,74.93169398907104 -20250225090119,Graph NN,0.02,0,0,0,2000,CPU,35.95067048072815,79.64481115341187 -20250225090119,GraphTM,0.02,10000,10.0,23,10,CUDA,120.58509016036987,97.8415300546448 -20250225090119,TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,976.5062215328217,71.9603825136612 -20250225090119,Graph NN,0.05,0,0,0,2000,CPU,49.44124245643616,76.09289884567261 -20250225090119,GraphTM,0.05,10000,10.0,23,10,CUDA,136.56197214126587,94.89071038251366 -20250225090119,TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,971.7774770259857,63.25136612021858 -20250225090119,Graph NN,0.1,0,0,0,2000,CPU,45.797210931777954,73.5792338848114 -20250225090119,GraphTM,0.1,10000,10.0,23,10,CUDA,149.67395901679993,89.23497267759562 -20250225090119,TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,979.9483859539032,49.59016393442623 -20250225090119,Graph NN,0.2,0,0,0,2000,CPU,41.42583513259888,68.27868819236755 -20250225090119,GraphTM,0.2,10000,10.0,23,10,CUDA,170.87367057800293,79.18032786885246 -20250225090119,TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,961.5906836986542,20.081967213114755 -20250225090119,Graph NN,0.005,0,0,0,2000,CPU,43.93612337112427,79.20765280723572 -20250225090119,GraphTM,0.005,10000,10.0,23,10,CUDA,109.73634815216064,98.63387978142076 -20250225090119,TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,965.0153555870056,76.63934426229508 -20250225090119,Graph NN,0.01,0,0,0,2000,CPU,49.67618227005005,92.45901703834534 -20250225090119,GraphTM,0.01,10000,10.0,23,10,CUDA,113.5588014125824,98.4153005464481 -20250225090119,TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,983.9533984661102,74.4535519125683 -20250225090119,Graph NN,0.02,0,0,0,2000,CPU,36.16115427017212,80.87431788444519 -20250225090119,GraphTM,0.02,10000,10.0,23,10,CUDA,120.58146834373474,97.81420765027322 -20250225090119,TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,944.9977910518646,71.51639344262296 -20250225090119,Graph NN,0.05,0,0,0,2000,CPU,48.164318561553955,83.77048969268799 -20250225090119,GraphTM,0.05,10000,10.0,23,10,CUDA,135.97020173072815,94.75409836065573 -20250225090119,TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,937.3424286842346,64.1051912568306 -20250225090119,Graph NN,0.1,0,0,0,2000,CPU,46.862754344940186,70.84699273109436 -20250225090119,GraphTM,0.1,10000,10.0,23,10,CUDA,149.9700825214386,89.72677595628416 -20250225090119,TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,990.8207032680511,49.86338797814208 -20250225090119,Graph NN,0.2,0,0,0,2000,CPU,48.0979220867157,63.66119980812073 -20250225090119,GraphTM,0.2,10000,10.0,23,10,CUDA,170.79332089424133,78.63387978142077 -20250225090119,TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,990.5863327980042,20.116120218579233 -20250225090119,Graph NN,0.005,0,0,0,2000,CPU,39.39827084541321,83.2513689994812 -20250225090119,GraphTM,0.005,10000,10.0,23,10,CUDA,109.96842241287231,98.52459016393442 -20250225090119,TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,941.0268743038177,76.63934426229508 -20250225090119,Graph NN,0.01,0,0,0,2000,CPU,37.69904541969299,93.44262480735779 +20250409090514,Graph NN,0.005,0,0,0,2000,CPU,47.380565881729126,84.23497080802917 +20250409090514,GraphTM,0.005,10000,10.0,23,10,CUDA,110.12741780281067,98.63387978142076 +20250409090514,TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,1190.9095215797424,77.11748633879782 +20250409090514,Graph NN,0.01,0,0,0,2000,CPU,49.00558853149414,92.65027046203613 +20250409090514,GraphTM,0.01,10000,10.0,23,10,CUDA,113.65191793441772,98.44262295081967 +20250409090514,TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,1020.6083555221558,74.86338797814209 +20250409090514,Graph NN,0.02,0,0,0,2000,CPU,44.6860625743866,77.13114619255066 +20250409090514,GraphTM,0.02,10000,10.0,23,10,CUDA,121.2872724533081,97.78688524590164 +20250409090514,TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,1246.0178999900818,72.40437158469946 +20250409090514,Graph NN,0.05,0,0,0,2000,CPU,82.58793544769287,88.46994638442993 +20250409090514,GraphTM,0.05,10000,10.0,23,10,CUDA,137.15939092636108,94.39890710382514 +20250409090514,TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,1317.742176771164,63.25136612021858 +20250409090514,Graph NN,0.1,0,0,0,2000,CPU,54.852065563201904,76.4207661151886 +20250409090514,GraphTM,0.1,10000,10.0,23,10,CUDA,151.09674072265625,89.89071038251366 +20250409090514,TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,1123.5956239700317,49.59016393442623 +20250409090514,Graph NN,0.2,0,0,0,2000,CPU,51.210848808288574,68.93442869186401 +20250409090514,GraphTM,0.2,10000,10.0,23,10,CUDA,170.72992277145386,78.5792349726776 +20250409090514,TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,1148.6567842960358,20.116120218579233 +20250409090514,Graph NN,0.005,0,0,0,2000,CPU,48.660605907440186,86.63934469223022 +20250409090514,GraphTM,0.005,10000,10.0,23,10,CUDA,110.17098808288574,98.82513661202185 +20250409090514,TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,1061.7185904979706,76.63934426229508 +20250409090514,Graph NN,0.01,0,0,0,2000,CPU,49.778627157211304,95.76502442359924 +20250409090514,GraphTM,0.01,10000,10.0,23,10,CUDA,113.88378477096558,98.4153005464481 +20250409090514,TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,1058.3029556274414,74.93169398907104 +20250409090514,Graph NN,0.02,0,0,0,2000,CPU,39.869826555252075,76.5573799610138 +20250409090514,GraphTM,0.02,10000,10.0,23,10,CUDA,120.86488842964172,97.6775956284153 +20250409090514,TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,1157.85533452034,72.40437158469946 +20250409090514,Graph NN,0.05,0,0,0,2000,CPU,39.27051615715027,80.21857738494873 +20250409090514,GraphTM,0.05,10000,10.0,23,10,CUDA,137.07859206199646,94.42622950819673 +20250409090514,TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,1060.4789934158325,64.1051912568306 +20250409090514,Graph NN,0.1,0,0,0,2000,CPU,41.18854546546936,78.032785654068 +20250409090514,GraphTM,0.1,10000,10.0,23,10,CUDA,150.01649594306946,89.86338797814207 +20250409090514,TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,1074.8029758930206,49.21448087431694 +20250409090514,Graph NN,0.2,0,0,0,2000,CPU,42.942272901535034,68.22404265403748 +20250409090514,GraphTM,0.2,10000,10.0,23,10,CUDA,170.39786314964294,78.0327868852459 +20250409090514,TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,1051.4041996002197,20.081967213114755 +20250409090514,Graph NN,0.005,0,0,0,2000,CPU,48.943641662597656,80.43715953826904 +20250409090514,GraphTM,0.005,10000,10.0,23,10,CUDA,111.18853044509888,98.82513661202185 +20250409090514,TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,1000.6668944358826,76.74180327868852 +20250409090514,Graph NN,0.01,0,0,0,2000,CPU,34.4648540019989,84.59016680717468 +20250409090514,GraphTM,0.01,10000,10.0,23,10,CUDA,113.77461814880371,98.27868852459017 +20250409090514,TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,1045.2479929924011,74.93169398907104 +20250409090514,Graph NN,0.02,0,0,0,2000,CPU,40.32768535614014,77.40437388420105 +20250409090514,GraphTM,0.02,10000,10.0,23,10,CUDA,120.75347566604614,97.8415300546448 +20250409090514,TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,1042.6038060188293,72.1311475409836 +20250409090514,Graph NN,0.05,0,0,0,2000,CPU,49.051427602767944,76.85792446136475 +20250409090514,GraphTM,0.05,10000,10.0,23,10,CUDA,135.81657576560974,94.89071038251366 +20250409090514,TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,1049.5465006828308,63.69535519125683 +20250409090514,Graph NN,0.1,0,0,0,2000,CPU,50.19066071510315,74.07103776931763 +20250409090514,GraphTM,0.1,10000,10.0,23,10,CUDA,150.23873829841614,89.69945355191257 +20250409090514,TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,1161.7163217067719,48.80464480874317 +20250409090514,Graph NN,0.2,0,0,0,2000,CPU,42.93249225616455,63.06011080741882 +20250409090514,GraphTM,0.2,10000,10.0,23,10,CUDA,169.8643877506256,79.20765027322403 +20250409090514,TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,968.4304020404816,20.116120218579233 +20250409090514,Graph NN,0.005,0,0,0,2000,CPU,46.011924266815186,80.24590015411377 +20250409090514,GraphTM,0.005,10000,10.0,23,10,CUDA,109.72403120994568,98.66120218579235 +20250409090514,TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,1174.494342327118,76.74180327868852 +20250409090514,Graph NN,0.01,0,0,0,2000,CPU,41.743159532547,80.02732396125793 +20250409090514,GraphTM,0.01,10000,10.0,23,10,CUDA,114.41021490097046,98.4153005464481 +20250409090514,TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,1171.6064977645874,74.93169398907104 +20250409090514,Graph NN,0.02,0,0,0,2000,CPU,44.349541664123535,87.45901584625244 +20250409090514,GraphTM,0.02,10000,10.0,23,10,CUDA,121.4791738986969,97.45901639344262 +20250409090514,TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,952.0120975971222,71.65300546448088 +20250409090514,Graph NN,0.05,0,0,0,2000,CPU,48.69317936897278,75.92896223068237 +20250409090514,GraphTM,0.05,10000,10.0,23,10,CUDA,136.3904469013214,94.4535519125683 +20250409090514,TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,969.868058681488,64.00273224043715 +20250409090514,Graph NN,0.1,0,0,0,2000,CPU,44.044572591781616,70.8743155002594 +20250409090514,GraphTM,0.1,10000,10.0,23,10,CUDA,149.6289074420929,89.8360655737705 +20250409090514,TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,953.6086061000824,50.10245901639344 +20250409090514,Graph NN,0.2,0,0,0,2000,CPU,44.549598932266235,61.284154653549194 +20250409090514,GraphTM,0.2,10000,10.0,23,10,CUDA,170.53832936286926,79.53551912568307 +20250409090514,TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,972.7086639404297,20.116120218579233 +20250409090514,Graph NN,0.005,0,0,0,2000,CPU,47.114877223968506,81.69398903846741 +20250409090514,GraphTM,0.005,10000,10.0,23,10,CUDA,109.53987145423889,98.68852459016394 +20250409090514,TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,957.2526223659515,76.63934426229508 +20250409090514,Graph NN,0.01,0,0,0,2000,CPU,37.89606070518494,85.65573692321777 +20250409090514,GraphTM,0.01,10000,10.0,23,10,CUDA,114.25655388832092,98.30601092896175 +20250409090514,TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,1173.4506571292877,74.93169398907104 +20250409090514,Graph NN,0.02,0,0,0,2000,CPU,47.68080997467041,83.36065411567688 +20250409090514,GraphTM,0.02,10000,10.0,23,10,CUDA,120.15364933013916,97.8688524590164 +20250409090514,TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,1153.5412156581879,72.1311475409836 +20250409090514,Graph NN,0.05,0,0,0,2000,CPU,41.10796904563904,83.41529965400696 +20250409090514,GraphTM,0.05,10000,10.0,23,10,CUDA,136.6818916797638,95.0 +20250409090514,TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,967.7902429103851,63.25136612021858 +20250409090514,Graph NN,0.1,0,0,0,2000,CPU,36.63528251647949,82.81420469284058 +20250409090514,GraphTM,0.1,10000,10.0,23,10,CUDA,150.54849863052368,89.31693989071037 +20250409090514,TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,965.3704278469086,49.62431693989071 +20250409090514,Graph NN,0.2,0,0,0,2000,CPU,40.28898596763611,64.61748480796814 +20250409090514,GraphTM,0.2,10000,10.0,23,10,CUDA,169.49659419059753,79.97267759562841 +20250409090514,TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,1158.38462972641,20.21857923497268 +20250409090514,Graph NN,0.005,0,0,0,2000,CPU,43.29892086982727,77.95081734657288 +20250409090514,GraphTM,0.005,10000,10.0,23,10,CUDA,110.77093839645386,98.68852459016394 +20250409090514,TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,944.0426867008209,76.63934426229508 +20250409090514,Graph NN,0.01,0,0,0,2000,CPU,40.48178577423096,91.17486476898193 +20250409090514,GraphTM,0.01,10000,10.0,23,10,CUDA,114.66628408432007,98.3879781420765 +20250409090514,TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,1151.3295328617096,74.93169398907104 +20250409090514,Graph NN,0.02,0,0,0,2000,CPU,46.342252254486084,91.42076373100281 +20250409090514,GraphTM,0.02,10000,10.0,23,10,CUDA,121.12805104255676,97.70491803278688 +20250409090514,TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,956.9201290607452,72.37021857923497 +20250409090514,Graph NN,0.05,0,0,0,2000,CPU,48.09459686279297,90.16393423080444 +20250409090514,GraphTM,0.05,10000,10.0,23,10,CUDA,136.35990571975708,94.31693989071039 +20250409090514,TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,951.3514447212219,64.00273224043715 +20250409090514,Graph NN,0.1,0,0,0,2000,CPU,47.61181974411011,77.04917788505554 +20250409090514,GraphTM,0.1,10000,10.0,23,10,CUDA,149.66685557365417,90.40983606557377 +20250409090514,TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,1159.4669754505157,49.62431693989071 +20250409090514,Graph NN,0.2,0,0,0,2000,CPU,40.52361035346985,61.666667461395264 +20250409090514,GraphTM,0.2,10000,10.0,23,10,CUDA,170.45302724838257,79.09836065573771 +20250409090514,TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,942.1310601234436,20.116120218579233 +20250409090514,Graph NN,0.005,0,0,0,2000,CPU,42.33190155029297,80.79234957695007 +20250409090514,GraphTM,0.005,10000,10.0,23,10,CUDA,110.67640900611877,98.82513661202185 +20250409090514,TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,1151.425032377243,76.63934426229508 +20250409090514,Graph NN,0.01,0,0,0,2000,CPU,46.83778142929077,79.94535565376282 +20250409090514,GraphTM,0.01,10000,10.0,23,10,CUDA,113.82480311393738,98.25136612021858 +20250409090514,TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,959.6910009384155,74.86338797814209 +20250409090514,Graph NN,0.02,0,0,0,2000,CPU,46.91451978683472,79.26229238510132 +20250409090514,GraphTM,0.02,10000,10.0,23,10,CUDA,121.25436019897461,97.81420765027322 +20250409090514,TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,973.5784142017365,72.1311475409836 +20250409090514,Graph NN,0.05,0,0,0,2000,CPU,45.216925859451294,79.56284284591675 +20250409090514,GraphTM,0.05,10000,10.0,23,10,CUDA,136.08299708366394,94.89071038251366 +20250409090514,TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,941.2294843196869,64.1051912568306 +20250409090514,Graph NN,0.1,0,0,0,2000,CPU,35.09868001937866,70.24590373039246 +20250409090514,GraphTM,0.1,10000,10.0,23,10,CUDA,150.008531332016,89.97267759562841 +20250409090514,TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,967.8004837036133,49.62431693989071 +20250409090514,Graph NN,0.2,0,0,0,2000,CPU,40.60944890975952,60.76502799987793 +20250409090514,GraphTM,0.2,10000,10.0,23,10,CUDA,170.61232328414917,78.52459016393442 +20250409090514,TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,1174.2148485183716,20.116120218579233 +20250409090514,Graph NN,0.005,0,0,0,2000,CPU,44.02885293960571,86.72131299972534 +20250409090514,GraphTM,0.005,10000,10.0,23,10,CUDA,110.33011960983276,98.82513661202185 +20250409090514,TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,1164.813972711563,76.74180327868852 +20250409090514,Graph NN,0.01,0,0,0,2000,CPU,34.82557439804077,91.83059930801392 +20250409090514,GraphTM,0.01,10000,10.0,23,10,CUDA,113.68903136253357,98.30601092896175 +20250409090514,TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,1142.874398946762,74.86338797814209 +20250409090514,Graph NN,0.02,0,0,0,2000,CPU,38.12274146080017,84.09836292266846 +20250409090514,GraphTM,0.02,10000,10.0,23,10,CUDA,120.88822174072266,97.89617486338797 +20250409090514,TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,958.9832980632782,72.60928961748634 +20250409090514,Graph NN,0.05,0,0,0,2000,CPU,47.38658022880554,83.63388180732727 +20250409090514,GraphTM,0.05,10000,10.0,23,10,CUDA,136.52869582176208,95.0 +20250409090514,TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,982.7437946796417,64.00273224043715 +20250409090514,Graph NN,0.1,0,0,0,2000,CPU,50.3098578453064,78.49726676940918 +20250409090514,GraphTM,0.1,10000,10.0,23,10,CUDA,150.58712220191956,90.10928961748634 +20250409090514,TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,952.3902399539948,48.97540983606557 +20250409090514,Graph NN,0.2,0,0,0,2000,CPU,47.68881940841675,67.54098534584045 +20250409090514,GraphTM,0.2,10000,10.0,23,10,CUDA,170.5669903755188,78.44262295081967 +20250409090514,TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,1160.643584728241,20.116120218579233 +20250409090514,Graph NN,0.005,0,0,0,2000,CPU,42.35506534576416,80.71038126945496 +20250409090514,GraphTM,0.005,10000,10.0,23,10,CUDA,110.72827911376953,98.46994535519126 +20250409090514,TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,1161.2603483200073,76.70765027322405 +20250409090514,Graph NN,0.01,0,0,0,2000,CPU,44.48380947113037,75.95628499984741 +20250409090514,GraphTM,0.01,10000,10.0,23,10,CUDA,113.78427290916443,98.4153005464481 +20250409090514,TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,1164.732885837555,74.93169398907104 +20250409090514,Graph NN,0.02,0,0,0,2000,CPU,41.45829200744629,88.27868700027466 +20250409090514,GraphTM,0.02,10000,10.0,23,10,CUDA,121.14582562446594,97.62295081967213 +20250409090514,TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,971.4570569992065,72.40437158469946 +20250409090514,Graph NN,0.05,0,0,0,2000,CPU,44.6593804359436,75.7377028465271 +20250409090514,GraphTM,0.05,10000,10.0,23,10,CUDA,136.09871077537537,94.72677595628414 +20250409090514,TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,1170.4177556037903,64.1051912568306 +20250409090514,Graph NN,0.1,0,0,0,2000,CPU,41.33125162124634,78.38797569274902 +20250409090514,GraphTM,0.1,10000,10.0,23,10,CUDA,150.5243456363678,89.61748633879782 +20250409090514,TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,940.6334030628204,49.62431693989071 +20250409090514,Graph NN,0.2,0,0,0,2000,CPU,43.79690456390381,63.387978076934814 +20250409090514,GraphTM,0.2,10000,10.0,23,10,CUDA,170.47909784317017,77.34972677595628 +20250409090514,TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,951.3798985481262,20.116120218579233 +20250409090514,Graph NN,0.005,0,0,0,2000,CPU,44.20913028717041,94.4535493850708 +20250409090514,GraphTM,0.005,10000,10.0,23,10,CUDA,110.41194748878479,98.82513661202185 +20250409090514,TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,1164.4012093544006,76.74180327868852 +20250409090514,Graph NN,0.01,0,0,0,2000,CPU,43.56287693977356,77.86885499954224 +20250409090514,GraphTM,0.01,10000,10.0,23,10,CUDA,113.86108899116516,98.25136612021858 +20250409090514,TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,1165.0554220676422,74.55601092896174 +20250409090514,Graph NN,0.02,0,0,0,2000,CPU,43.18827676773071,90.76502919197083 +20250409090514,GraphTM,0.02,10000,10.0,23,10,CUDA,121.40065360069275,97.6775956284153 +20250409090514,TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,1142.509984254837,72.1311475409836 +20250409090514,Graph NN,0.05,0,0,0,2000,CPU,46.11475706100464,87.2950792312622 +20250409090514,GraphTM,0.05,10000,10.0,23,10,CUDA,136.23513627052307,93.98907103825137 +20250409090514,TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,1018.888409614563,64.1051912568306 +20250409090514,Graph NN,0.1,0,0,0,2000,CPU,46.72879457473755,72.92349934577942 +20250409090514,GraphTM,0.1,10000,10.0,23,10,CUDA,150.53106451034546,89.75409836065575 +20250409090514,TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,1152.1242747306824,49.62431693989071 +20250409090514,Graph NN,0.2,0,0,0,2000,CPU,42.78840351104736,61.72131299972534 +20250409090514,GraphTM,0.2,10000,10.0,23,10,CUDA,170.45607113838196,78.5792349726776 +20250409090514,TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,1150.6887817382812,20.184426229508194 diff --git a/examples/recomm_system/experiment_results_ensamble.csv b/examples/recomm_system/experiment_results_ensamble.csv deleted file mode 100644 index b394dad6..00000000 --- a/examples/recomm_system/experiment_results_ensamble.csv +++ /dev/null @@ -1,181 +0,0 @@ -Algorithm,Noise_Ratio,T,s,Max_Included_Literals,Epochs,Platform,Total_Time,Accuracy -Graph NN,0.005,0,0,0,20000,CPU,418.9250466823578,75.62841773033142 -GraphTM,0.005,10000,10.0,23,10,CUDA,110.35683226585388,98.68852459016394 -TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,1059.1634759902954,76.81010928961749 -Graph NN,0.01,0,0,0,20000,CPU,550.6980571746826,94.50819492340088 -GraphTM,0.01,10000,10.0,23,10,CUDA,114.06276345252991,98.4153005464481 -TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,1051.916612625122,75.3415300546448 -Graph NN,0.02,0,0,0,20000,CPU,475.44024682044983,75.30054450035095 -GraphTM,0.02,10000,10.0,23,10,CUDA,121.55624794960022,97.8415300546448 -TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,1043.9487817287445,72.1311475409836 -Graph NN,0.05,0,0,0,20000,CPU,411.8552327156067,80.98360896110535 -GraphTM,0.05,10000,10.0,23,10,CUDA,136.7814338207245,94.20765027322405 -TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,1044.2656917572021,64.00273224043715 -Graph NN,0.1,0,0,0,20000,CPU,484.6550889015198,68.7158465385437 -GraphTM,0.1,10000,10.0,23,10,CUDA,150.34457921981812,89.72677595628416 -TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,1061.191523551941,49.62431693989071 -Graph NN,0.2,0,0,0,20000,CPU,483.8463816642761,71.28415107727051 -GraphTM,0.2,10000,10.0,23,10,CUDA,169.18810439109802,78.49726775956285 -TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,1071.927158355713,20.116120218579233 -Graph NN,0.005,0,0,0,20000,CPU,473.5806052684784,86.36612296104431 -GraphTM,0.005,10000,10.0,23,10,CUDA,110.18979954719543,98.60655737704917 -TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,979.0509588718414,76.74180327868852 -Graph NN,0.01,0,0,0,20000,CPU,444.6897065639496,93.55190992355347 -GraphTM,0.01,10000,10.0,23,10,CUDA,112.48035550117493,98.4153005464481 -TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,1007.9654748439789,74.86338797814209 -Graph NN,0.02,0,0,0,20000,CPU,386.32835030555725,93.22404265403748 -GraphTM,0.02,10000,10.0,23,10,CUDA,120.46316766738892,97.73224043715847 -TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,1017.5866801738739,73.25819672131148 -Graph NN,0.05,0,0,0,20000,CPU,417.78410935401917,73.1693983078003 -GraphTM,0.05,10000,10.0,23,10,CUDA,135.64952206611633,95.08196721311475 -TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,945.0465729236603,64.00273224043715 -Graph NN,0.1,0,0,0,20000,CPU,481.6537721157074,77.18579173088074 -GraphTM,0.1,10000,10.0,23,10,CUDA,149.57958960533142,90.08196721311475 -TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,938.0212676525116,49.59016393442623 -Graph NN,0.2,0,0,0,20000,CPU,391.36059975624084,64.15300369262695 -GraphTM,0.2,10000,10.0,23,10,CUDA,169.49347591400146,77.65027322404372 -TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,940.9758951663971,20.116120218579233 -Graph NN,0.005,0,0,0,20000,CPU,480.5005066394806,75.68305730819702 -GraphTM,0.005,10000,10.0,23,10,CUDA,109.65927052497864,98.19672131147541 -TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,947.7581994533539,76.63934426229508 -Graph NN,0.01,0,0,0,20000,CPU,449.22584795951843,76.36612057685852 -GraphTM,0.01,10000,10.0,23,10,CUDA,113.07226181030273,98.27868852459017 -TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,1010.8711988925934,74.93169398907104 -Graph NN,0.02,0,0,0,20000,CPU,403.96647000312805,96.85792326927185 -GraphTM,0.02,10000,10.0,23,10,CUDA,120.02044725418091,97.78688524590164 -TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,1011.7896072864532,72.40437158469946 -Graph NN,0.05,0,0,0,20000,CPU,460.688773393631,85.00000238418579 -GraphTM,0.05,10000,10.0,23,10,CUDA,136.04891228675842,94.69945355191257 -TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,1014.1492829322815,64.00273224043715 -Graph NN,0.1,0,0,0,20000,CPU,407.9346880912781,74.1256833076477 -GraphTM,0.1,10000,10.0,23,10,CUDA,149.6586093902588,90.08196721311475 -TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,990.8282098770142,49.59016393442623 -Graph NN,0.2,0,0,0,20000,CPU,437.8108870983124,65.60109257698059 -GraphTM,0.2,10000,10.0,23,10,CUDA,168.44772601127625,78.93442622950819 -TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,1022.1848647594452,20.116120218579233 -Graph NN,0.005,0,0,0,20000,CPU,430.3925087451935,89.20764923095703 -GraphTM,0.005,10000,10.0,23,10,CUDA,109.6658935546875,98.68852459016394 -TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,1016.199923992157,76.63934426229508 -Graph NN,0.01,0,0,0,20000,CPU,396.3338620662689,84.23497080802917 -GraphTM,0.01,10000,10.0,23,10,CUDA,113.67849016189575,98.27868852459017 -TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,944.4602844715118,74.86338797814209 -Graph NN,0.02,0,0,0,20000,CPU,434.91951632499695,93.25136542320251 -GraphTM,0.02,10000,10.0,23,10,CUDA,120.31921482086182,97.8415300546448 -TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,933.2245874404907,72.40437158469946 -Graph NN,0.05,0,0,0,20000,CPU,483.2671537399292,80.32786846160889 -GraphTM,0.05,10000,10.0,23,10,CUDA,135.68922591209412,94.78142076502732 -TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,994.6384744644165,64.13934426229508 -Graph NN,0.1,0,0,0,20000,CPU,424.9935986995697,81.33879899978638 -GraphTM,0.1,10000,10.0,23,10,CUDA,149.08107113838196,89.59016393442623 -TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,944.0273253917694,49.62431693989071 -Graph NN,0.2,0,0,0,20000,CPU,333.49274706840515,61.50273084640503 -GraphTM,0.2,10000,10.0,23,10,CUDA,170.906751871109,78.98907103825137 -TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,965.9725024700165,20.116120218579233 -Graph NN,0.005,0,0,0,20000,CPU,377.28471970558167,75.68305730819702 -GraphTM,0.005,10000,10.0,23,10,CUDA,109.61631536483765,98.82513661202185 -TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,976.8008840084076,76.67349726775956 -Graph NN,0.01,0,0,0,20000,CPU,473.2922372817993,76.06557607650757 -GraphTM,0.01,10000,10.0,23,10,CUDA,112.87212014198303,98.27868852459017 -TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,942.7254059314728,74.86338797814209 -Graph NN,0.02,0,0,0,20000,CPU,357.36573815345764,75.40983557701111 -GraphTM,0.02,10000,10.0,23,10,CUDA,119.41612005233765,97.8415300546448 -TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,985.81947016716,72.40437158469946 -Graph NN,0.05,0,0,0,20000,CPU,440.75843334198,73.08743000030518 -GraphTM,0.05,10000,10.0,23,10,CUDA,136.8215868473053,94.91803278688525 -TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,997.739678144455,64.1051912568306 -Graph NN,0.1,0,0,0,20000,CPU,426.73446226119995,88.55191469192505 -GraphTM,0.1,10000,10.0,23,10,CUDA,149.54467248916626,89.94535519125682 -TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,980.096907377243,49.59016393442623 -Graph NN,0.2,0,0,0,20000,CPU,387.20843958854675,75.71038007736206 -GraphTM,0.2,10000,10.0,23,10,CUDA,169.7962884902954,77.56830601092896 -TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,987.0616261959076,20.116120218579233 -Graph NN,0.005,0,0,0,20000,CPU,455.586905002594,83.41529965400696 -GraphTM,0.005,10000,10.0,23,10,CUDA,109.7424705028534,98.5792349726776 -TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,998.4698519706726,76.81010928961749 -Graph NN,0.01,0,0,0,20000,CPU,466.44022035598755,98.52458834648132 -GraphTM,0.01,10000,10.0,23,10,CUDA,112.78495740890503,98.27868852459017 -TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,932.3163437843323,74.93169398907104 -Graph NN,0.02,0,0,0,20000,CPU,455.35024762153625,88.96175026893616 -GraphTM,0.02,10000,10.0,23,10,CUDA,120.741384267807,97.75956284153006 -TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,974.3740100860596,72.1311475409836 -Graph NN,0.05,0,0,0,20000,CPU,399.9565739631653,73.60655665397644 -GraphTM,0.05,10000,10.0,23,10,CUDA,136.17181992530823,94.67213114754098 -TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,971.1499485969543,64.1051912568306 -Graph NN,0.1,0,0,0,20000,CPU,447.5498752593994,70.8743155002594 -GraphTM,0.1,10000,10.0,23,10,CUDA,149.6928951740265,89.80874316939891 -TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,962.4737737178802,49.59016393442623 -Graph NN,0.2,0,0,0,20000,CPU,403.6350507736206,64.15300369262695 -GraphTM,0.2,10000,10.0,23,10,CUDA,170.02189421653748,78.16939890710383 -TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,947.2696743011475,20.116120218579233 -Graph NN,0.005,0,0,0,20000,CPU,470.0121097564697,81.20218515396118 -GraphTM,0.005,10000,10.0,23,10,CUDA,109.51706099510193,98.82513661202185 -TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,974.2360310554504,76.74180327868852 -Graph NN,0.01,0,0,0,20000,CPU,466.69573068618774,76.06557607650757 -GraphTM,0.01,10000,10.0,23,10,CUDA,112.95063591003418,98.4153005464481 -TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,948.407201051712,74.86338797814209 -Graph NN,0.02,0,0,0,20000,CPU,288.0073969364166,92.92349815368652 -GraphTM,0.02,10000,10.0,23,10,CUDA,119.34772634506226,97.48633879781421 -TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,971.228814125061,39.51502732240437 -Graph NN,0.05,0,0,0,20000,CPU,477.7228500843048,89.86338973045349 -GraphTM,0.05,10000,10.0,23,10,CUDA,135.2427453994751,94.86338797814207 -TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,964.5819866657257,34.56284153005464 -Graph NN,0.1,0,0,0,20000,CPU,459.15181946754456,71.22950553894043 -GraphTM,0.1,10000,10.0,23,10,CUDA,148.52941298484802,89.67213114754098 -TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,981.4810082912445,49.59016393442623 -Graph NN,0.2,0,0,0,20000,CPU,356.59899377822876,64.15300369262695 -GraphTM,0.2,10000,10.0,23,10,CUDA,169.7598683834076,76.85792349726775 -TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,959.9282560348511,20.116120218579233 -Graph NN,0.005,0,0,0,20000,CPU,378.94336581230164,80.32786846160889 -GraphTM,0.005,10000,10.0,23,10,CUDA,109.55144882202148,98.44262295081967 -TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,947.1284465789795,76.63934426229508 -Graph NN,0.01,0,0,0,20000,CPU,407.1111581325531,94.31694149971008 -GraphTM,0.01,10000,10.0,23,10,CUDA,112.06348276138306,98.27868852459017 -TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,941.711061000824,39.65163934426229 -Graph NN,0.02,0,0,0,20000,CPU,402.2970163822174,79.80874180793762 -GraphTM,0.02,10000,10.0,23,10,CUDA,120.20444130897522,97.8415300546448 -TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,998.2885782718658,72.1311475409836 -Graph NN,0.05,0,0,0,20000,CPU,400.97751235961914,85.30054688453674 -GraphTM,0.05,10000,10.0,23,10,CUDA,136.81029963493347,94.78142076502732 -TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,1003.2194263935089,64.1051912568306 -Graph NN,0.1,0,0,0,20000,CPU,413.25741934776306,74.59016442298889 -GraphTM,0.1,10000,10.0,23,10,CUDA,148.70455861091614,89.89071038251366 -TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,974.4099938869476,49.62431693989071 -Graph NN,0.2,0,0,0,20000,CPU,369.36416029930115,64.15300369262695 -GraphTM,0.2,10000,10.0,23,10,CUDA,170.01750564575195,78.55191256830601 -TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,990.2080008983612,20.184426229508194 -Graph NN,0.005,0,0,0,20000,CPU,440.5256702899933,90.4644787311554 -GraphTM,0.005,10000,10.0,23,10,CUDA,109.76434278488159,98.55191256830601 -TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,1004.704318523407,76.63934426229508 -Graph NN,0.01,0,0,0,20000,CPU,385.76011848449707,77.62295007705688 -GraphTM,0.01,10000,10.0,23,10,CUDA,113.28425002098083,98.44262295081967 -TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,953.8945541381836,74.93169398907104 -Graph NN,0.02,0,0,0,20000,CPU,422.2995481491089,90.71038365364075 -GraphTM,0.02,10000,10.0,23,10,CUDA,121.29091334342957,97.6775956284153 -TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,1002.099497795105,72.1311475409836 -Graph NN,0.05,0,0,0,20000,CPU,383.33483958244324,81.8306028842926 -GraphTM,0.05,10000,10.0,23,10,CUDA,134.72863698005676,94.53551912568307 -TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,936.831921339035,64.1051912568306 -Graph NN,0.1,0,0,0,20000,CPU,320.32143545150757,83.60655903816223 -GraphTM,0.1,10000,10.0,23,10,CUDA,150.56500816345215,89.15300546448087 -TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,955.8687121868134,49.62431693989071 -Graph NN,0.2,0,0,0,20000,CPU,432.34014868736267,64.15300369262695 -GraphTM,0.2,10000,10.0,23,10,CUDA,169.61127710342407,79.12568306010928 -TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,945.0617082118988,20.116120218579233 -Graph NN,0.005,0,0,0,20000,CPU,458.87039852142334,79.37158346176147 -GraphTM,0.005,10000,10.0,23,10,CUDA,110.9952290058136,98.82513661202185 -TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,985.8775904178619,76.63934426229508 -Graph NN,0.01,0,0,0,20000,CPU,453.55728340148926,76.06557607650757 -GraphTM,0.01,10000,10.0,23,10,CUDA,113.85269451141357,98.27868852459017 -TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,941.0662143230438,75.06830601092896 -Graph NN,0.02,0,0,0,20000,CPU,416.2407822608948,91.66666865348816 -GraphTM,0.02,10000,10.0,23,10,CUDA,120.69959592819214,97.78688524590164 -TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,973.9127674102783,72.40437158469946 -Graph NN,0.05,0,0,0,20000,CPU,311.2621831893921,75.46448111534119 -GraphTM,0.05,10000,10.0,23,10,CUDA,134.66055345535278,94.89071038251366 -TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,953.3380017280579,63.25136612021858 -Graph NN,0.1,0,0,0,20000,CPU,425.43416261672974,73.79781603813171 -GraphTM,0.1,10000,10.0,23,10,CUDA,150.67951107025146,90.27322404371586 -TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,967.5897221565247,49.59016393442623 -Graph NN,0.2,0,0,0,20000,CPU,379.8497235774994,64.15300369262695 -GraphTM,0.2,10000,10.0,23,10,CUDA,169.7126281261444,77.81420765027323 -TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,955.9427745342255,20.116120218579233 diff --git a/examples/recomm_system/experiment_results_old.csv b/examples/recomm_system/experiment_results_old.csv deleted file mode 100644 index f715ba6a..00000000 --- a/examples/recomm_system/experiment_results_old.csv +++ /dev/null @@ -1,271 +0,0 @@ -Algorithm,Noise_Ratio,T,s,Max_Included_Literals,Epochs,Platform,Total_Time,Accuracy -Graph NN,0.005,0,0,0,1000,CPU,0.03006434440612793,76.72131061553955 -GraphTM,0.005,10000,10.0,23,10,CUDA,34.547648191452026,98.46994535519126 -TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,89.6943154335022,76.63934426229508 -Graph NN,0.01,0,0,0,1000,CPU,0.01817464828491211,75.95628499984741 -GraphTM,0.01,10000,10.0,23,10,CUDA,34.95576763153076,98.44262295081967 -TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,96.10501098632812,74.93169398907104 -Graph NN,0.02,0,0,0,1000,CPU,0.03073263168334961,81.22950792312622 -GraphTM,0.02,10000,10.0,23,10,CUDA,36.0724892616272,97.43169398907104 -TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,95.67133641242981,72.40437158469946 -Graph NN,0.05,0,0,0,1000,CPU,0.014258623123168945,83.52459073066711 -GraphTM,0.05,10000,10.0,23,10,CUDA,38.86628317832947,95.0 -TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,96.7427487373352,64.65163934426229 -Graph NN,0.1,0,0,0,1000,CPU,0.022305965423583984,73.33333492279053 -GraphTM,0.1,10000,10.0,23,10,CUDA,37.45086216926575,90.08196721311475 -TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,90.45554423332214,49.8292349726776 -Graph NN,0.2,0,0,0,1000,CPU,0.03204679489135742,59.863388538360596 -GraphTM,0.2,10000,10.0,23,10,CUDA,16.268279790878296,78.77049180327869 -TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,96.16712856292725,20.184426229508194 -Graph NN,0.005,0,0,0,1000,CPU,0.0168764591217041,76.85792446136475 -GraphTM,0.005,10000,10.0,23,10,CUDA,31.40691065788269,98.82513661202185 -TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,88.05298614501953,76.74180327868852 -Graph NN,0.01,0,0,0,1000,CPU,0.01720118522644043,87.4316930770874 -GraphTM,0.01,10000,10.0,23,10,CUDA,31.529547214508057,98.4153005464481 -TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,89.19472336769104,74.93169398907104 -Graph NN,0.02,0,0,0,1000,CPU,0.014032602310180664,78.36065292358398 -GraphTM,0.02,10000,10.0,23,10,CUDA,32.8007595539093,97.62295081967213 -TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,94.56675243377686,72.6775956284153 -Graph NN,0.05,0,0,0,1000,CPU,0.016784191131591797,76.88524723052979 -GraphTM,0.05,10000,10.0,23,10,CUDA,34.84256434440613,94.75409836065573 -TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,96.4975814819336,64.1051912568306 -Graph NN,0.1,0,0,0,1000,CPU,0.014883041381835938,70.54644823074341 -GraphTM,0.1,10000,10.0,23,10,CUDA,36.750433683395386,89.97267759562841 -TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,96.35110449790955,50.341530054644814 -Graph NN,0.2,0,0,0,1000,CPU,0.03427433967590332,61.50273084640503 -GraphTM,0.2,10000,10.0,23,10,CUDA,39.63756251335144,79.01639344262294 -TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,97.00698733329773,20.116120218579233 -Graph NN,0.005,0,0,0,20000,CPU,370.7295939922333,87.5683069229126 -GraphTM,0.005,10000,10.0,23,10,CUDA,342.7878243923187,98.82513661202185 -TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,954.4101324081421,76.63934426229508 -Graph NN,0.01,0,0,0,20000,CPU,304.6031119823456,86.74863576889038 -GraphTM,0.01,10000,10.0,23,10,CUDA,346.8704605102539,98.25136612021858 -TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,978.3629264831543,74.93169398907104 -Graph NN,0.02,0,0,0,20000,CPU,403.2585175037384,75.30054450035095 -GraphTM,0.02,10000,10.0,23,10,CUDA,353.39254236221313,97.65027322404372 -TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,971.3300836086273,72.1311475409836 -Graph NN,0.05,0,0,0,20000,CPU,398.8085067272186,93.8524603843689 -GraphTM,0.05,10000,10.0,23,10,CUDA,368.16111874580383,94.59016393442623 -TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,960.4506890773773,63.661202185792355 -Graph NN,0.1,0,0,0,20000,CPU,388.4886665344238,75.43715834617615 -GraphTM,0.1,10000,10.0,23,10,CUDA,340.63327074050903,90.43715846994536 -TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,972.1077370643616,49.35109289617486 -Graph NN,0.2,0,0,0,20000,CPU,438.5506749153137,64.04371857643127 -GraphTM,0.2,10000,10.0,23,10,CUDA,357.2651107311249,77.89617486338798 -TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,948.7157049179077,20.116120218579233 -Graph NN,0.005,0,0,0,20000,CPU,335.8319003582001,94.97267603874207 -GraphTM,0.005,10000,10.0,23,10,CUDA,343.08735728263855,98.63387978142076 -TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,947.0340785980225,76.74180327868852 -Graph NN,0.01,0,0,0,20000,CPU,380.5575759410858,94.37158703804016 -GraphTM,0.01,10000,10.0,23,10,CUDA,346.9574134349823,98.4153005464481 -TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,948.3826260566711,74.93169398907104 -Graph NN,0.02,0,0,0,20000,CPU,317.0974416732788,80.6010901927948 -GraphTM,0.02,10000,10.0,23,10,CUDA,352.5908226966858,97.5136612021858 -TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,966.0719907283783,72.40437158469946 -Graph NN,0.05,0,0,0,20000,CPU,472.30924010276794,73.08743000030518 -GraphTM,0.05,10000,10.0,23,10,CUDA,352.63378834724426,94.18032786885246 -TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,959.5826976299286,64.31010928961749 -Graph NN,0.1,0,0,0,20000,CPU,461.1769962310791,82.45901465415955 -GraphTM,0.1,10000,10.0,23,10,CUDA,384.25392842292786,89.80874316939891 -TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,968.517664194107,49.62431693989071 -Graph NN,0.2,0,0,0,20000,CPU,338.83801436424255,61.39343976974487 -GraphTM,0.2,10000,10.0,23,10,CUDA,406.0366141796112,79.37158469945356 -TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,956.5074710845947,20.116120218579233 -Graph NN,0.005,0,0,0,20000,CPU,449.974244594574,99.07103776931763 -GraphTM,0.005,10000,10.0,23,10,CUDA,110.82642030715942,98.63387978142076 -TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,958.8415122032166,76.63934426229508 -Graph NN,0.01,0,0,0,20000,CPU,340.71677923202515,91.557377576828 -GraphTM,0.01,10000,10.0,23,10,CUDA,113.30413746833801,98.4153005464481 -TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,954.5596807003021,74.86338797814209 -Graph NN,0.02,0,0,0,20000,CPU,395.9958527088165,90.95628261566162 -GraphTM,0.02,10000,10.0,23,10,CUDA,120.9222981929779,97.8415300546448 -TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,969.4929764270782,72.40437158469946 -Graph NN,0.05,0,0,0,20000,CPU,480.05427837371826,84.83606576919556 -GraphTM,0.05,10000,10.0,23,10,CUDA,135.44805693626404,94.67213114754098 -TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,960.4112854003906,64.00273224043715 -Graph NN,0.1,0,0,0,20000,CPU,383.12051796913147,70.8743155002594 -GraphTM,0.1,10000,10.0,23,10,CUDA,149.93119883537292,89.86338797814207 -TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,951.469316482544,49.35109289617486 -Graph NN,0.2,0,0,0,20000,CPU,463.9883725643158,66.22951030731201 -GraphTM,0.2,10000,10.0,23,10,CUDA,170.47470378875732,78.16939890710383 -TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,960.5258178710938,20.116120218579233 -Graph NN,0.005,0,0,0,20000,CPU,475.9830324649811,82.54098296165466 -GraphTM,0.005,10000,10.0,23,10,CUDA,109.21395993232727,98.7431693989071 -TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,1007.2876415252686,76.74180327868852 -Graph NN,0.01,0,0,0,20000,CPU,383.468213558197,84.89071130752563 -GraphTM,0.01,10000,10.0,23,10,CUDA,112.81892561912537,98.16939890710383 -TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,1012.9538216590881,75.0 -Graph NN,0.02,0,0,0,20000,CPU,420.129834651947,78.87977957725525 -GraphTM,0.02,10000,10.0,23,10,CUDA,120.55745768547058,97.75956284153006 -TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,1015.7262468338013,72.43852459016394 -Graph NN,0.05,0,0,0,20000,CPU,402.9082715511322,88.90710473060608 -GraphTM,0.05,10000,10.0,23,10,CUDA,136.1779272556305,94.69945355191257 -TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,1003.5450174808502,64.1051912568306 -Graph NN,0.1,0,0,0,20000,CPU,465.9741690158844,71.61202430725098 -GraphTM,0.1,10000,10.0,23,10,CUDA,150.92307353019714,90.21857923497268 -TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,993.3001370429993,49.62431693989071 -Graph NN,0.2,0,0,0,20000,CPU,477.5556457042694,61.967211961746216 -GraphTM,0.2,10000,10.0,23,10,CUDA,170.91576671600342,78.71584699453553 -TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,968.9711816310883,20.116120218579233 -Graph NN,0.005,0,0,0,20000,CPU,432.9368100166321,87.9781424999237 -GraphTM,0.005,10000,10.0,23,10,CUDA,110.05442261695862,98.4153005464481 -TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,996.241945028305,77.11748633879782 -Graph NN,0.01,0,0,0,20000,CPU,487.0275945663452,76.06557607650757 -GraphTM,0.01,10000,10.0,23,10,CUDA,114.20750546455383,98.27868852459017 -TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,965.2801012992859,74.89754098360656 -Graph NN,0.02,0,0,0,20000,CPU,469.96120142936707,84.61748361587524 -GraphTM,0.02,10000,10.0,23,10,CUDA,120.18490934371948,97.62295081967213 -TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,996.0747539997101,73.05327868852459 -Graph NN,0.05,0,0,0,20000,CPU,391.52739334106445,94.4535493850708 -GraphTM,0.05,10000,10.0,23,10,CUDA,135.62234830856323,94.89071038251366 -TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,1008.111634016037,64.00273224043715 -Graph NN,0.1,0,0,0,20000,CPU,393.4089164733887,82.24043846130371 -GraphTM,0.1,10000,10.0,23,10,CUDA,150.06821942329407,90.21857923497268 -TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,1029.8733656406403,46.89207650273224 -Graph NN,0.2,0,0,0,20000,CPU,457.90059518814087,64.50819969177246 -GraphTM,0.2,10000,10.0,23,10,CUDA,169.8122251033783,78.5792349726776 -TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,994.4631915092468,20.116120218579233 -Graph NN,0.005,0,0,0,20000,CPU,468.43779039382935,93.66120100021362 -GraphTM,0.005,10000,10.0,23,10,CUDA,791.0080873966217,98.66120218579235 -TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,1003.8278872966766,76.63934426229508 -Graph NN,0.01,0,0,0,20000,CPU,432.6524693965912,76.06557607650757 -GraphTM,0.01,10000,10.0,23,10,CUDA,114.20011568069458,98.4153005464481 -TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,1002.5212485790253,74.55601092896174 -Graph NN,0.02,0,0,0,20000,CPU,369.3357195854187,77.92349457740784 -GraphTM,0.02,10000,10.0,23,10,CUDA,120.16606998443604,97.78688524590164 -TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,1012.7241668701172,72.40437158469946 -Graph NN,0.05,0,0,0,20000,CPU,425.18350195884705,73.49726557731628 -GraphTM,0.05,10000,10.0,23,10,CUDA,134.74739480018616,94.53551912568307 -TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,989.5920696258545,64.00273224043715 -Graph NN,0.1,0,0,0,20000,CPU,490.52463579177856,74.23497438430786 -GraphTM,0.1,10000,10.0,23,10,CUDA,150.663067817688,90.05464480874316 -TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,1006.5979704856873,49.86338797814208 -Graph NN,0.2,0,0,0,20000,CPU,430.0901610851288,55.51912784576416 -GraphTM,0.2,10000,10.0,23,10,CUDA,169.53561758995056,78.52459016393442 -TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,978.9952318668365,20.116120218579233 -Graph NN,0.005,0,0,0,20000,CPU,348.87419414520264,88.87978196144104 -GraphTM,0.005,10000,10.0,23,10,CUDA,110.35069704055786,98.49726775956285 -TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,1027.553718328476,76.63934426229508 -Graph NN,0.01,0,0,0,20000,CPU,459.8675227165222,94.97267603874207 -GraphTM,0.01,10000,10.0,23,10,CUDA,113.4369592666626,98.4153005464481 -TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,1020.3086180686951,74.93169398907104 -Graph NN,0.02,0,0,0,20000,CPU,402.3793728351593,98.08743000030518 -GraphTM,0.02,10000,10.0,23,10,CUDA,121.04798412322998,97.78688524590164 -TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,1001.1654710769653,72.1311475409836 -Graph NN,0.05,0,0,0,20000,CPU,372.8648886680603,77.81420946121216 -GraphTM,0.05,10000,10.0,23,10,CUDA,135.42569255828857,94.78142076502732 -TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,1007.9532639980316,64.31010928961749 -Graph NN,0.1,0,0,0,20000,CPU,379.2149317264557,88.55191469192505 -GraphTM,0.1,10000,10.0,23,10,CUDA,149.3813440799713,89.50819672131148 -TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,1043.259267091751,49.65846994535519 -Graph NN,0.2,0,0,0,20000,CPU,327.1461730003357,64.15300369262695 -GraphTM,0.2,10000,10.0,23,10,CUDA,169.63331365585327,77.75956284153006 -TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,1010.9232707023621,20.081967213114755 -Graph NN,0.005,0,0,0,20000,CPU,365.3540139198303,84.56284403800964 -GraphTM,0.005,10000,10.0,23,10,CUDA,109.86703443527222,98.55191256830601 -TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,985.2240512371063,76.84426229508196 -Graph NN,0.01,0,0,0,20000,CPU,419.19047832489014,90.65573811531067 -GraphTM,0.01,10000,10.0,23,10,CUDA,113.7970187664032,98.19672131147541 -TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,1064.9949398040771,75.27322404371584 -Graph NN,0.02,0,0,0,20000,CPU,331.5898778438568,82.13114738464355 -GraphTM,0.02,10000,10.0,23,10,CUDA,120.9221625328064,97.8415300546448 -TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,995.9801988601685,72.1311475409836 -Graph NN,0.05,0,0,0,20000,CPU,471.61706471443176,76.4207661151886 -GraphTM,0.05,10000,10.0,23,10,CUDA,135.71256685256958,94.31693989071039 -TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,1049.4032156467438,64.00273224043715 -Graph NN,0.1,0,0,0,20000,CPU,408.78746509552,75.76502561569214 -GraphTM,0.1,10000,10.0,23,10,CUDA,150.7326798439026,89.86338797814207 -TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,1033.6956369876862,49.59016393442623 -Graph NN,0.2,0,0,0,20000,CPU,606.6176900863647,75.84699392318726 -GraphTM,0.2,10000,10.0,23,10,CUDA,767.3086304664612,79.18032786885246 -TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,1133.7219278812408,20.116120218579233 -Graph NN,0.005,0,0,0,20000,CPU,581.8730342388153,75.68305730819702 -GraphTM,0.005,10000,10.0,23,10,CUDA,331.4337913990021,98.68852459016394 -TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,1139.0209171772003,76.63934426229508 -Graph NN,0.01,0,0,0,20000,CPU,625.7649390697479,79.91803288459778 -GraphTM,0.01,10000,10.0,23,10,CUDA,390.8302972316742,98.27868852459017 -TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,1126.1463103294373,74.86338797814209 -Graph NN,0.02,0,0,0,20000,CPU,400.4486656188965,88.87978196144104 -GraphTM,0.02,10000,10.0,23,10,CUDA,1433.5869204998016,97.73224043715847 -TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,950.7241444587708,72.43852459016394 -Graph NN,0.05,0,0,0,20000,CPU,425.54064321517944,88.22404146194458 -GraphTM,0.05,10000,10.0,23,10,CUDA,135.85678553581238,95.0 -TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,980.2039513587952,40.26639344262295 -Graph NN,0.1,0,0,0,20000,CPU,452.5277452468872,75.38251280784607 -GraphTM,0.1,10000,10.0,23,10,CUDA,149.88782930374146,89.80874316939891 -TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,1026.7852320671082,49.59016393442623 -Graph NN,0.2,0,0,0,20000,CPU,470.88474774360657,69.67213153839111 -GraphTM,0.2,10000,10.0,23,10,CUDA,169.65682435035706,78.38797814207649 -TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,1025.9789564609528,20.116120218579233 -Graph NN,0.005,0,0,0,20000,CPU,415.4326367378235,75.68305730819702 -GraphTM,0.005,10000,10.0,23,10,CUDA,109.78167200088501,98.82513661202185 -TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,1003.7237763404846,76.63934426229508 -Graph NN,0.01,0,0,0,20000,CPU,444.45101857185364,92.65027046203613 -GraphTM,0.01,10000,10.0,23,10,CUDA,112.90761637687683,98.14207650273225 -TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,952.2491714954376,74.86338797814209 -Graph NN,0.02,0,0,0,20000,CPU,320.37370920181274,93.90710592269897 -GraphTM,0.02,10000,10.0,23,10,CUDA,119.93352174758911,97.78688524590164 -TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,943.684113740921,72.40437158469946 -Graph NN,0.05,0,0,0,20000,CPU,481.5506682395935,73.08743000030518 -GraphTM,0.05,10000,10.0,23,10,CUDA,135.72563362121582,94.75409836065573 -TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,1023.0283312797546,63.661202185792355 -Graph NN,0.1,0,0,0,20000,CPU,493.5546169281006,70.92896103858948 -GraphTM,0.1,10000,10.0,23,10,CUDA,149.45619106292725,89.80874316939891 -TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,1015.6581709384918,49.62431693989071 -Graph NN,0.2,0,0,0,20000,CPU,413.9959945678711,64.15300369262695 -GraphTM,0.2,10000,10.0,23,10,CUDA,170.9294879436493,78.77049180327869 -TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,986.7937209606171,20.116120218579233 -Graph NN,0.005,0,0,0,20000,CPU,378.53097796440125,75.68305730819702 -GraphTM,0.005,10000,10.0,23,10,CUDA,110.98681783676147,98.30601092896175 -TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,1019.9160070419312,76.74180327868852 -Graph NN,0.01,0,0,0,20000,CPU,474.00093841552734,91.0109281539917 -GraphTM,0.01,10000,10.0,23,10,CUDA,111.94242978096008,98.4153005464481 -TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,999.8850407600403,75.03415300546447 -Graph NN,0.02,0,0,0,20000,CPU,346.5858099460602,79.3169379234314 -GraphTM,0.02,10000,10.0,23,10,CUDA,120.32013273239136,97.81420765027322 -TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,937.4906117916107,71.92622950819673 -Graph NN,0.05,0,0,0,20000,CPU,408.48123002052307,79.61748838424683 -GraphTM,0.05,10000,10.0,23,10,CUDA,134.27622246742249,94.72677595628414 -TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,998.1966772079468,64.1051912568306 -Graph NN,0.1,0,0,0,20000,CPU,373.10851979255676,70.8743155002594 -GraphTM,0.1,10000,10.0,23,10,CUDA,148.95248794555664,89.86338797814207 -TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,993.9887461662292,49.62431693989071 -Graph NN,0.2,0,0,0,20000,CPU,388.21142077445984,64.15300369262695 -GraphTM,0.2,10000,10.0,23,10,CUDA,168.77049660682678,76.93989071038251 -TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,947.7270972728729,20.116120218579233 -Graph NN,0.005,0,0,0,20000,CPU,370.7274992465973,75.79234838485718 -GraphTM,0.005,10000,10.0,23,10,CUDA,109.92479467391968,98.27868852459017 -TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,944.8954434394836,76.74180327868852 -Graph NN,0.01,0,0,0,20000,CPU,382.68008041381836,90.8196747303009 -GraphTM,0.01,10000,10.0,23,10,CUDA,113.02455401420593,98.4153005464481 -TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,958.4739623069763,74.86338797814209 -Graph NN,0.02,0,0,0,20000,CPU,466.3325071334839,96.22950553894043 -GraphTM,0.02,10000,10.0,23,10,CUDA,121.06816530227661,97.6775956284153 -TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,952.7009084224701,72.1311475409836 -Graph NN,0.05,0,0,0,20000,CPU,462.6835868358612,75.79234838485718 -GraphTM,0.05,10000,10.0,23,10,CUDA,136.21898555755615,94.53551912568307 -TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,974.2475302219391,64.00273224043715 -Graph NN,0.1,0,0,0,20000,CPU,425.79654932022095,87.18579411506653 -GraphTM,0.1,10000,10.0,23,10,CUDA,149.70053339004517,90.0 -TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,1005.1148529052734,49.59016393442623 -Graph NN,0.2,0,0,0,20000,CPU,454.7309219837189,60.10928750038147 -GraphTM,0.2,10000,10.0,23,10,CUDA,170.75228261947632,78.68852459016394 -TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,949.2937788963318,20.116120218579233 -Graph NN,0.005,0,0,0,20000,CPU,570.247394323349,75.68305730819702 -GraphTM,0.005,10000,10.0,23,10,CUDA,478.04068207740784,98.5792349726776 -TMClassifier,0.005,10000,10.0,32,10,CPU_sparse,1043.9900722503662,76.22950819672131 -Graph NN,0.01,0,0,0,20000,CPU,428.5804445743561,98.68852496147156 -GraphTM,0.01,10000,10.0,23,10,CUDA,522.4638862609863,98.44262295081967 -TMClassifier,0.01,10000,10.0,32,10,CPU_sparse,1060.4919381141663,74.93169398907104 -Graph NN,0.02,0,0,0,20000,CPU,432.5051038265228,76.25682950019836 -GraphTM,0.02,10000,10.0,23,10,CUDA,465.56538343429565,97.73224043715847 -TMClassifier,0.02,10000,10.0,32,10,CPU_sparse,1074.2418582439423,72.40437158469946 -Graph NN,0.05,0,0,0,20000,CPU,492.7251534461975,85.16393303871155 -GraphTM,0.05,10000,10.0,23,10,CUDA,688.4105927944183,94.91803278688525 -TMClassifier,0.05,10000,10.0,32,10,CPU_sparse,1055.8136265277863,64.00273224043715 -Graph NN,0.1,0,0,0,20000,CPU,584.2829260826111,78.22404503822327 -GraphTM,0.1,10000,10.0,23,10,CUDA,625.4286091327667,90.13661202185791 -TMClassifier,0.1,10000,10.0,32,10,CPU_sparse,1055.7545056343079,48.83879781420765 -Graph NN,0.2,0,0,0,20000,CPU,318.2997555732727,67.40437150001526 -GraphTM,0.2,10000,10.0,23,10,CUDA,1264.404123544693,77.62295081967213 -TMClassifier,0.2,10000,10.0,32,10,CPU_sparse,1000.3779845237732,20.081967213114755 diff --git a/examples/recomm_system/test.ipynb b/examples/recomm_system/test.ipynb index 07b30e69..bf5c1fac 100644 --- a/examples/recomm_system/test.ipynb +++ b/examples/recomm_system/test.ipynb @@ -67,7 +67,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 4, "metadata": {}, "outputs": [ { @@ -80,12 +80,12 @@ "\\begin{tabular}{|c|c|c|c|}\n", "\\hline\n", "\\textbf{Noise Ratio} & \\textbf{GCN (\\%)} & \\textbf{GTM (\\%)} & \\textbf{TMClassifier (\\%)} \\\\ \\hline\n", - "0.005 & 83.36 & 98.67 & 76.67 \\\\ \\hline\n", - "0.01 & 86.13 & 98.42 & 74.58 \\\\ \\hline\n", - "0.02 & 85.03 & 97.75 & 72.10 \\\\ \\hline\n", - "0.05 & 79.93 & 94.80 & 63.87 \\\\ \\hline\n", - "0.1 & 73.73 & 89.73 & 49.44 \\\\ \\hline\n", - "0.2 & 65.86 & 78.48 & 20.11 \\\\ \\hline\n", + "0.005 & 83.39 & 98.73 & 76.73 \\\\ \\hline\n", + "0.01 & 85.55 & 98.35 & 74.87 \\\\ \\hline\n", + "0.02 & 83.57 & 97.73 & 72.24 \\\\ \\hline\n", + "0.05 & 82.13 & 94.61 & 63.86 \\\\ \\hline\n", + "0.1 & 75.93 & 89.85 & 49.48 \\\\ \\hline\n", + "0.2 & 64.12 & 78.73 & 20.13 \\\\ \\hline\n", "\\end{tabular}\n", "\\caption{Average accuracy comparison of GCN, GraphTM, and TMClassifier for varying noise ratios.}\n", "\\label{tab:recomm_sys_accuracy}\n", @@ -96,7 +96,7 @@ "source": [ "import pandas as pd\n", "data = pd.read_csv(\"experiment_results.csv\")\n", - "exp_id = \"20250225090119\" \n", + "exp_id = \"20250409090514\" \n", "data['Exp_id'] = data['Exp_id'].astype(str)\n", "filtered_data = data[data['Exp_id'] == exp_id]\n", "# print(filtered_data)\n", From cedabda4f099e8a2ceaf0870b511647cdf7dfb17 Mon Sep 17 00:00:00 2001 From: Ahmed Khalid Date: Sat, 10 May 2025 08:46:21 +0000 Subject: [PATCH 28/33] calc total time --- examples/recomm_system/test.ipynb | 45 +++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/examples/recomm_system/test.ipynb b/examples/recomm_system/test.ipynb index bf5c1fac..320a30bf 100644 --- a/examples/recomm_system/test.ipynb +++ b/examples/recomm_system/test.ipynb @@ -132,6 +132,51 @@ "\n", "print(latex_table)" ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Averages across all noise ratios:\n", + "Algorithm: Graph NN, Average Accuracy: 79.11%, Average Total Time: 44.80s\n", + "Algorithm: GraphTM, Average Accuracy: 93.00%, Average Total Time: 133.75s\n", + "Algorithm: TMClassifier, Average Accuracy: 59.55%, Average Total Time: 1068.99s\n" + ] + } + ], + "source": [ + "import pandas as pd\n", + "\n", + "# Read the CSV file\n", + "data = pd.read_csv(\"experiment_results.csv\")\n", + "\n", + "# Define the experiment ID you want to filter\n", + "exp_id = \"20250409090514\"\n", + "\n", + "# Ensure that Exp_id is treated as a string\n", + "data['Exp_id'] = data['Exp_id'].astype(str)\n", + "\n", + "# Filter the data based on the experiment ID\n", + "filtered_data = data[data['Exp_id'] == exp_id]\n", + "\n", + "# Group the data by Algorithm to calculate average accuracies and total time across all noise ratios\n", + "grouped_data = filtered_data.groupby('Algorithm').agg({'Accuracy': 'mean', 'Total_Time': 'mean'}).reset_index()\n", + "\n", + "# Print the average results for each algorithm across all noise ratios\n", + "print(\"Averages across all noise ratios:\")\n", + "for _, row in grouped_data.iterrows():\n", + " algorithm = row['Algorithm']\n", + " average_accuracy = row['Accuracy']\n", + " average_total_time = row['Total_Time']\n", + " \n", + " # Print the results\n", + " print(f\"Algorithm: {algorithm}, Average Accuracy: {average_accuracy:.2f}%, Average Total Time: {average_total_time:.2f}s\")\n" + ] } ], "metadata": { From 14b0b689ac6559ec51f5dd91ecc8d1f06d13fe7a Mon Sep 17 00:00:00 2001 From: Ahmed Khalid Date: Tue, 20 May 2025 11:10:28 +0000 Subject: [PATCH 29/33] prepare as sup. mat. --- examples/recomm_system/README.md | 17 +++- .../recomm_system/experiment_results.xlsx | Bin 0 -> 33687 bytes examples/recomm_system/main.py | 36 ++++++++ examples/recomm_system/test.ipynb | 82 ++++++++++++++++++ 4 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 examples/recomm_system/experiment_results.xlsx create mode 100644 examples/recomm_system/main.py diff --git a/examples/recomm_system/README.md b/examples/recomm_system/README.md index e7fa211a..c03a4deb 100644 --- a/examples/recomm_system/README.md +++ b/examples/recomm_system/README.md @@ -1,2 +1,17 @@ +# Recommender System Experiments + +**How to run:** +```sh cd examples/recomm_system/ -bash main.sh \ No newline at end of file +python3 main.py +``` + +**Files:** +- `main.py` — Runs all experiments, calls each model script for various noise ratios, saves results to `experiment_results.csv`. +- `graph_nn.py` — Graph Neural Network (GCN) experiment. +- `graph_tm.py` — Graph Tsetlin Machine experiment. +- `tm_classifier.py` — Tsetlin Machine Classifier experiment. +- `prepare_dataset.py` — Dataset download, noise injection, preprocessing. +- `experiment_results.csv` — Results log (auto-generated). +- `test.ipynb` — Summarizes results, generates LaTeX tables. + diff --git a/examples/recomm_system/experiment_results.xlsx b/examples/recomm_system/experiment_results.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..54f854981d2bdf3ad205a300b352aca0698f15f7 GIT binary patch literal 33687 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
-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 wLt5|OOTO0tuiuQnA)sAf>*J4(^-qMWzk38YV7& Date: Tue, 18 Nov 2025 13:04:31 +0000 Subject: [PATCH 30/33] add explainable exp --- .gitignore | 4 - GraphTsetlinMachine/kernels.py | 1 - GraphTsetlinMachine/tm.py | 3 +- LICENSE | 2 +- README.md | 4 +- .../prepare_dataset.cpython-310.pyc | Bin 5371 -> 5348 bytes examples/recomm_system/graph_tm_explain.py | 146 ++++++++++++++++++ setup.py | 2 +- 8 files changed, 151 insertions(+), 11 deletions(-) create mode 100644 examples/recomm_system/graph_tm_explain.py diff --git a/.gitignore b/.gitignore index 9f5bc781..9a53b815 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,3 @@ -build/ -GraphTsetlinMachine.egg-info/ -/dist/ - .envrc # Byte-compiled / optimized / DLL files diff --git a/GraphTsetlinMachine/kernels.py b/GraphTsetlinMachine/kernels.py index 54ffd433..2594c48a 100644 --- a/GraphTsetlinMachine/kernels.py +++ b/GraphTsetlinMachine/kernels.py @@ -787,7 +787,6 @@ unsigned int *local_ta_state = &ta_state[clause * chunks * STATE_BITS]; for (int literal = 0; literal < literals; ++literal) { - unsigned int state = 0; int chunk_nr = literal / INT_SIZE; int chunk_pos = literal % INT_SIZE; out[clause * literals + literal] = (local_ta_state[chunk_nr * STATE_BITS + STATE_BITS - 1] & (1 << chunk_pos)) > 0; diff --git a/GraphTsetlinMachine/tm.py b/GraphTsetlinMachine/tm.py index 2db8fc21..d402916d 100644 --- a/GraphTsetlinMachine/tm.py +++ b/GraphTsetlinMachine/tm.py @@ -75,7 +75,6 @@ def __init__( self.number_of_state_bits = number_of_state_bits self.message_size = message_size self.message_bits = message_bits - self.message_literals = message_size*2 self.double_hashing = double_hashing self.one_hot_encoding = one_hot_encoding @@ -541,7 +540,7 @@ def _init_fit(self, graphs, encoded_Y, incremental): self.current_clause_node_output_train_gpu = cuda.mem_alloc(int(self.number_of_clauses * graphs.max_number_of_graph_node_chunks) * 4) self.next_clause_node_output_train_gpu = cuda.mem_alloc(int(self.number_of_clauses * graphs.max_number_of_graph_node_chunks) * 4) - self.clause_X_int_train_gpu = cuda.mem_alloc(int(graphs.max_number_of_graph_nodes * self.message_literals) * 4) + self.clause_X_int_train_gpu = cuda.mem_alloc(int(graphs.max_number_of_graph_nodes * self.number_of_message_literals) * 4) self.clause_X_train_gpu = [] for depth in range(self.depth-1): diff --git a/LICENSE b/LICENSE index 07732506..77e6b3bf 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2024 Ole-Christoffer Granmo +Copyright (c) 2025 Ole-Christoffer Granmo and University of Agder Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 42040a2f..6ddd9444 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ pip3 install graphtsetlinmachine or ```bash python ./setup.py sdist -pip3 install dist/GraphTsetlinMachine-0.3.1.tar.gz +pip3 install dist/GraphTsetlinMachine-0.3.4.tar.gz ``` ## Tutorial @@ -339,7 +339,7 @@ tm = MultiClassGraphTsetlinMachine( ## Licence -Copyright (c) 2024 Ole-Christoffer Granmo +Copyright (c) 2025 Ole-Christoffer Granmo and University of Agder Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/examples/recomm_system/__pycache__/prepare_dataset.cpython-310.pyc b/examples/recomm_system/__pycache__/prepare_dataset.cpython-310.pyc index 8a2cd18398677e0c9bd2a85d805bb970ed3cb003..359048333a440a694927013b30d5a34a45a23fd5 100644 GIT binary patch delta 54 zcmeyZ`9zaDpO=@50SIg~b2f5&GRf%aCl(awr&c897UZNB>ldXa=jZ0e7grXSq~>mJ IXDSf_0IrM?XaE2J delta 77 zcmaE&`CF4apO=@50SLNYXKm#6WHNTuPb?_VPpwGIEyzhN261vSlM_oa^Ye=J3yShn ZN|Q^9 0 and node_id < graphs_train.number_of_graph_nodes[graph_id]-1 else 1 + if node_id == 0: + graphs_train.add_graph_node(graph_id, "User", number_of_edges) + elif node_id == 1: + graphs_train.add_graph_node(graph_id, "Item", number_of_edges) + else: + graphs_train.add_graph_node(graph_id, "Category", number_of_edges) + graphs_train.prepare_edge_configuration() + for graph_id in range(X_train.shape[0]): + for node_id in range(graphs_train.number_of_graph_nodes[graph_id]): + if node_id == 0: + graphs_train.add_graph_node_edge(graph_id, "User", "Item", "UserItem") + + if node_id == 1: + graphs_train.add_graph_node_edge(graph_id, "Item", "Category", "ItemCategory") + graphs_train.add_graph_node_edge(graph_id, "Item", "User", "ItemUser") + + if node_id == 2: + graphs_train.add_graph_node_edge(graph_id, "Category", "Item", "CatrgoryItem") + + graphs_train.add_graph_node_property(graph_id, "User", "U_" + str(X_train[graph_id][0])) + graphs_train.add_graph_node_property(graph_id, "Item", "I_" + str(X_train[graph_id][1])) + graphs_train.add_graph_node_property(graph_id, "Category", "C_" + str(X_train[graph_id][2])) + graphs_train.encode() + print("Training data produced") + + tm = MultiClassGraphTsetlinMachine( + args.number_of_clauses, + args.T, + args.s, + number_of_state_bits = args.number_of_state_bits, + depth=args.depth, + message_size=args.message_size, + message_bits=args.message_bits, + max_included_literals=args.max_included_literals, + double_hashing = args.double_hashing + ) + + for epoch in range(args.epochs): + tm.fit(graphs_train, Y_train, epochs=1, incremental=True) + + # print_clause_explanations(tm, graphs_train) + + state = tm.get_state() + clause_weights_flat = state[1] + number_of_classes = int(state[2]) + number_of_clauses = int(state[3]) + weights = clause_weights_flat.reshape(number_of_classes, number_of_clauses) + # weights = tm.get_state()[1].reshape(2, -1) + # Get Clauses in symbols format and Messages in clause_indices format + clause_literals = tm.get_clause_literals(graphs_train.hypervectors).astype(np.int32) + num_symbols = len(graphs_train.symbol_id) + + # Create symbol_id to symbol_name dictionary for printing symbol names + symbol_dict = dict((v, k) for k, v in graphs_train.symbol_id.items()) + + threshold = 7 + for target_label_of_Y in np.unique(Y_train): + print(f"Target label: {target_label_of_Y}, Number of positive clauses: {np.sum(weights[target_label_of_Y]>0)}") + for clause in range(tm.number_of_clauses): + if weights[target_label_of_Y, clause] > 0: + for literal in range(num_symbols): + state = clause_literals[clause, literal] + neg_state = clause_literals[clause, literal + num_symbols] + if np.any(state > threshold) or np.any(neg_state > threshold): + print(f"Clause {clause} [{weights[target_label_of_Y, clause]:>4d}]", end=": ") + if state > threshold: + print(f"{clause_literals[clause, literal]}{symbol_dict[literal]}", end=" ") + + if neg_state > threshold: + print(f"~{clause_literals[clause, literal + num_symbols]}{symbol_dict[literal]}", end=" ") + + print("") + +def default_args(**kwargs): + parser = argparse.ArgumentParser() + parser.add_argument("--epochs", default=1, type=int) + parser.add_argument("--number-of-clauses", default=2000, type=int) + parser.add_argument("--T", default=10000, type=int) + parser.add_argument("--s", default=10.0, type=float) + parser.add_argument("--number-of-state-bits", default=8, type=int) + parser.add_argument("--depth", default=1, type=int) + parser.add_argument("--hypervector-size", default=4096, type=int) + parser.add_argument("--hypervector-bits", default=256, type=int) + parser.add_argument("--message-size", default=256, type=int) + parser.add_argument("--message-bits", default=2, type=int) + parser.add_argument('--double-hashing', dest='double_hashing', default=False, action='store_true') + parser.add_argument("--noise", default=0.01, type=float) + parser.add_argument("--max-included-literals", default=23, type=int) + parser.add_argument("--dataset_noise_ratio", default=0.01, type=float) + parser.add_argument("--exp_id", default="", type=str) + args = parser.parse_args() + for key, value in kwargs.items(): + if key in args.__dict__: + setattr(args, key, value) + return args + +if __name__ == "__main__": + # train + main(default_args()) + + # run just explanation from saved model + # print_clause_explanations() \ No newline at end of file diff --git a/setup.py b/setup.py index 2be1e8bb..82007812 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ setup( name='GraphTsetlinMachine', - version='0.3.3', + version='0.3.4', author='Ole-Christoffer Granmo', author_email='ole.granmo@uia.no', url='https://github.com/cair/GraphTsetlinMachine/', From 587f09df7556ce81e570682eda3134aeecbf0997 Mon Sep 17 00:00:00 2001 From: Ahmed Khalid Date: Tue, 18 Nov 2025 13:07:35 +0000 Subject: [PATCH 31/33] ploting --- examples/recomm_system/graph_tm_explain.py | 53 ++++++++++++++++----- plots/class_0_top15.png | Bin 0 -> 21788 bytes plots/class_1_top15.png | Bin 0 -> 21594 bytes plots/class_2_top15.png | Bin 0 -> 21627 bytes plots/class_3_top15.png | Bin 0 -> 23237 bytes plots/class_4_top15.png | Bin 0 -> 21707 bytes 6 files changed, 41 insertions(+), 12 deletions(-) create mode 100644 plots/class_0_top15.png create mode 100644 plots/class_1_top15.png create mode 100644 plots/class_2_top15.png create mode 100644 plots/class_3_top15.png create mode 100644 plots/class_4_top15.png diff --git a/examples/recomm_system/graph_tm_explain.py b/examples/recomm_system/graph_tm_explain.py index 99f2b497..071bdc8e 100644 --- a/examples/recomm_system/graph_tm_explain.py +++ b/examples/recomm_system/graph_tm_explain.py @@ -3,6 +3,8 @@ import argparse import numpy as np import prepare_dataset +import os +import matplotlib.pyplot as plt def main(args): @@ -98,22 +100,49 @@ def main(args): symbol_dict = dict((v, k) for k, v in graphs_train.symbol_id.items()) threshold = 7 + + # create output folder for plots + os.makedirs("plots", exist_ok=True) + top_n = 15 # number of top symbols to show per class + for target_label_of_Y in np.unique(Y_train): - print(f"Target label: {target_label_of_Y}, Number of positive clauses: {np.sum(weights[target_label_of_Y]>0)}") + # Aggregate scores per symbol across positive clauses for this class + scores = np.zeros(num_symbols, dtype=float) for clause in range(tm.number_of_clauses): - if weights[target_label_of_Y, clause] > 0: - for literal in range(num_symbols): - state = clause_literals[clause, literal] - neg_state = clause_literals[clause, literal + num_symbols] - if np.any(state > threshold) or np.any(neg_state > threshold): - print(f"Clause {clause} [{weights[target_label_of_Y, clause]:>4d}]", end=": ") - if state > threshold: - print(f"{clause_literals[clause, literal]}{symbol_dict[literal]}", end=" ") + w = weights[target_label_of_Y, clause] + if w <= 0: + continue + for literal in range(num_symbols): + state = clause_literals[clause, literal] + neg_state = clause_literals[clause, literal + num_symbols] + # handle scalar or array states + included_pos = np.any(state > threshold) if hasattr(state, "__iter__") else (state > threshold) + included_neg = np.any(neg_state > threshold) if hasattr(neg_state, "__iter__") else (neg_state > threshold) + if included_pos: + scores[literal] += w + if included_neg: + scores[literal] -= w + + # select top symbols by absolute aggregate score + idx_sorted = np.argsort(-np.abs(scores)) + top_idx = idx_sorted[:top_n] + labels = [symbol_dict[i] for i in top_idx] + vals = scores[top_idx] - if neg_state > threshold: - print(f"~{clause_literals[clause, literal + num_symbols]}{symbol_dict[literal]}", end=" ") + # Plot horizontal bar chart + plt.figure(figsize=(8, max(4, top_n * 0.35))) + colors = ['tab:green' if v > 0 else 'tab:red' for v in vals] + # reverse for descending plotting top->bottom + plt.barh(range(len(vals)), vals[::-1], color=[c for c in colors[::-1]]) + plt.yticks(range(len(vals)), labels[::-1], fontsize=8) + plt.xlabel('Aggregate clause weight') + plt.title(f'Top {top_n} symbols for class {target_label_of_Y}') + plt.tight_layout() - print("") + out_path = f"plots/class_{target_label_of_Y}_top{top_n}.png" + plt.savefig(out_path) + plt.close() + print(f"Saved plot: {out_path}") def default_args(**kwargs): parser = argparse.ArgumentParser() diff --git a/plots/class_0_top15.png b/plots/class_0_top15.png new file mode 100644 index 0000000000000000000000000000000000000000..8df141e82f706774801bff9867d74fb9b54e6e43 GIT binary patch literal 21788 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 RG2T+B;xfnLkDR^o{{ZRQPg(!~ literal 0 HcmV?d00001 diff --git a/plots/class_3_top15.png b/plots/class_3_top15.png new file mode 100644 index 0000000000000000000000000000000000000000..d67877960798849291986964ceb0dcde2daf5bc5 GIT binary patch literal 23237 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 literal 0 HcmV?d00001 diff --git a/plots/class_4_top15.png b/plots/class_4_top15.png new file mode 100644 index 0000000000000000000000000000000000000000..c25cec84318b72bfca22cd0a43174d4a8e8f6ab5 GIT binary patch literal 21707 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 Date: Sat, 29 Nov 2025 10:09:46 +0000 Subject: [PATCH 32/33] fix graph nn --- examples/recomm_system/graph_nn.py | 71 +++++++++++++++++++++++++----- 1 file changed, 61 insertions(+), 10 deletions(-) diff --git a/examples/recomm_system/graph_nn.py b/examples/recomm_system/graph_nn.py index ef6922a7..5ace5cca 100644 --- a/examples/recomm_system/graph_nn.py +++ b/examples/recomm_system/graph_nn.py @@ -7,17 +7,61 @@ from tmu.tools import BenchmarkTimer import os import pandas as pd +from sklearn.preprocessing import OneHotEncoder +import numpy as np def main(args): results = [] - data = prepare_dataset.aug_amazon_products(noise_ratio = args.dataset_noise_ratio) + data = prepare_dataset.aug_amazon_products(noise_ratio=args.dataset_noise_ratio) x, y = prepare_dataset.construct_x_y(data) - X_train, X_test, Y_train, Y_test = prepare_dataset.train_test_split(x,y) - # Graph Construction - num_users = len(data['user_id'].unique()) - num_items = len(data['product_id'].unique()) - num_categories = len(data['category'].unique()) + X_train, X_test, Y_train, Y_test = prepare_dataset.train_test_split(x, y) + + # Extract categorical identifiers for features + user_ids = data['user_id'].unique() + item_ids = data['product_id'].unique() + category_ids = data['category'].unique() + + print("Unique user_ids: ", len(user_ids)) + print("Unique item_ids: ", len(item_ids)) + print("Unique category_ids: ", len(category_ids)) + + num_users = len(user_ids) + num_items = len(item_ids) + num_categories = len(category_ids) num_nodes = num_users + num_items + num_categories + + # One-hot encoding for node features + user_encoder = OneHotEncoder() + item_encoder = OneHotEncoder() + category_encoder = OneHotEncoder() + + # Fit encoders using unique identifiers + user_features = user_encoder.fit_transform(user_ids.reshape(-1, 1)).toarray() # Convert to dense + item_features = item_encoder.fit_transform(item_ids.reshape(-1, 1)).toarray() # Convert to dense + category_features = category_encoder.fit_transform(category_ids.reshape(-1, 1)).toarray() # Convert to dense + + print("User features shape: ", user_features.shape) + print("Item features shape: ", item_features.shape) + print("Category features shape: ", category_features.shape) + + # Ensure consistent feature dimensions + max_feature_dim = max(user_features.shape[1], item_features.shape[1], category_features.shape[1]) + + # Pad features if dimension mismatch + if user_features.shape[1] < max_feature_dim: + user_features = np.pad(user_features, ((0, 0), (0, max_feature_dim - user_features.shape[1])), 'constant') + if item_features.shape[1] < max_feature_dim: + item_features = np.pad(item_features, ((0, 0), (0, max_feature_dim - item_features.shape[1])), 'constant') + if category_features.shape[1] < max_feature_dim: + category_features = np.pad(category_features, ((0, 0), (0, max_feature_dim - category_features.shape[1])), 'constant') + + # Concatenate all node features into a single tensor + node_features = torch.cat([ + torch.tensor(user_features, dtype=torch.float), + torch.tensor(item_features, dtype=torch.float), + torch.tensor(category_features, dtype=torch.float) + ], dim=0) + # Build edge list edge_list = [] # User ↔ Item edges @@ -30,25 +74,28 @@ def main(args): edge_list.append((num_users + num_items + category, num_users + item)) # Category to Item # Create edge index for PyTorch Geometric edge_index = torch.tensor(edge_list, dtype=torch.long).t() - # Node features - node_features = torch.rand((num_nodes, 64), dtype=torch.float) + # PyTorch Geometric Data object graph_data = Data(x=node_features, edge_index=edge_index) + # Step 2: Define GCN Model class GCN(torch.nn.Module): def __init__(self, input_dim, hidden_dim, output_dim): super(GCN, self).__init__() self.conv1 = GCNConv(input_dim, hidden_dim) self.conv2 = GCNConv(hidden_dim, output_dim) + def forward(self, x, edge_index): x = self.conv1(x, edge_index) x = F.relu(x) x = self.conv2(x, edge_index) return x + # Initialize Model - model = GCN(input_dim=64, hidden_dim=128, output_dim=64) + model = GCN(input_dim=node_features.shape[1], hidden_dim=128, output_dim=64) # Define optimizer optimizer = torch.optim.Adam(model.parameters(), lr=0.01) + # Convert train/test data to tensors train_edges = torch.tensor( [(user, num_users + item) for user, item in zip(X_train[:, 0], X_train[:, 1])], @@ -60,6 +107,7 @@ def forward(self, x, edge_index): dtype=torch.long ).t() test_labels = torch.tensor(Y_test, dtype=torch.float) + # Training Loop with Accuracy Logging benchmark_total = BenchmarkTimer(logger=None, text="Epochs Time") with benchmark_total: @@ -79,6 +127,7 @@ def forward(self, x, edge_index): loss.backward() optimizer.step() train_time = benchmark1.elapsed() + # Testing Phase benchmark2 = BenchmarkTimer(logger=None, text="Testing Time") with benchmark2: @@ -91,7 +140,9 @@ def forward(self, x, edge_index): # Compute accuracy accuracy = ((test_predicted_ratings.round() == test_labels).float().mean().item()) * 100 test_time = benchmark2.elapsed() + total_time = benchmark_total.elapsed() + # Append results for each epoch results.append({ "Exp_id": args.exp_id, @@ -129,4 +180,4 @@ def default_args(**kwargs): return args if __name__ == "__main__": - main(default_args()) \ No newline at end of file + main(default_args()) From 9f7c00fa0d37064124a3bad943b2561c53f11163 Mon Sep 17 00:00:00 2001 From: Ahmed Khalid Date: Mon, 1 Dec 2025 08:41:36 +0000 Subject: [PATCH 33/33] ploting --- examples/recomm_system/graph_tm_explain.py | 150 +++++++++++++----- .../all_classes_top15_comparison_compact.png | Bin 0 -> 101867 bytes plots/class_0_top15.png | Bin 21788 -> 44338 bytes plots/class_1_top15.png | Bin 21594 -> 44845 bytes plots/class_2_top15.png | Bin 21627 -> 44495 bytes plots/class_3_top15.png | Bin 23237 -> 44941 bytes plots/class_4_top15.png | Bin 21707 -> 44842 bytes 7 files changed, 109 insertions(+), 41 deletions(-) create mode 100644 plots/all_classes_top15_comparison_compact.png diff --git a/examples/recomm_system/graph_tm_explain.py b/examples/recomm_system/graph_tm_explain.py index 071bdc8e..0aceb18a 100644 --- a/examples/recomm_system/graph_tm_explain.py +++ b/examples/recomm_system/graph_tm_explain.py @@ -80,12 +80,9 @@ def main(args): max_included_literals=args.max_included_literals, double_hashing = args.double_hashing ) - for epoch in range(args.epochs): tm.fit(graphs_train, Y_train, epochs=1, incremental=True) - # print_clause_explanations(tm, graphs_train) - state = tm.get_state() clause_weights_flat = state[1] number_of_classes = int(state[2]) @@ -99,15 +96,46 @@ def main(args): # Create symbol_id to symbol_name dictionary for printing symbol names symbol_dict = dict((v, k) for k, v in graphs_train.symbol_id.items()) - threshold = 7 - # create output folder for plots os.makedirs("plots", exist_ok=True) top_n = 15 # number of top symbols to show per class + threshold = 7 # threshold for literal inclusion + + class_labels = np.unique(Y_train) + num_classes = len(class_labels) + + # --- Determine optimal figure and subplot spacing --- + # Calculate dynamic height based on number of classes and top_n to avoid too much whitespace + # Adjusted multiplier for height + row_height_per_feature = 0.25 # Smaller value to reduce vertical space per feature + fig_height = max(3.5 * num_classes, num_classes * top_n * row_height_per_feature + 2) + + fig, axes = plt.subplots( + nrows=num_classes, + ncols=2, + figsize=(10, fig_height), # Slightly reduced width, dynamically calculated height + sharey='row', # Share the Y-axis (symbol labels) only within each row + gridspec_kw={ + 'wspace': 0.15, # Reduced horizontal space between subplots + 'hspace': 0.45, # Reduced vertical space between rows of subplots + 'left': 0.1, # Adjust left margin + 'right': 0.95, # Adjust right margin + 'top': 0.93, # Adjust top margin (for suptitle) + 'bottom': 0.05 # Adjust bottom margin (for common x-labels) + } + ) - for target_label_of_Y in np.unique(Y_train): - # Aggregate scores per symbol across positive clauses for this class - scores = np.zeros(num_symbols, dtype=float) + # Ensure 'axes' is a 2D array even for a single class (num_classes=1) + if num_classes == 1: + axes = np.array([axes]) + + # --- Main Plotting Loop --- + for i, target_label_of_Y in enumerate(class_labels): + + # 1. Aggregate scores for the current class (Aggregation Logic remains the same) + state_scores = np.zeros(num_symbols, dtype=float) + weight_scores = np.zeros(num_symbols, dtype=float) + for clause in range(tm.number_of_clauses): w = weights[target_label_of_Y, clause] if w <= 0: @@ -115,41 +143,85 @@ def main(args): for literal in range(num_symbols): state = clause_literals[clause, literal] neg_state = clause_literals[clause, literal + num_symbols] - # handle scalar or array states - included_pos = np.any(state > threshold) if hasattr(state, "__iter__") else (state > threshold) - included_neg = np.any(neg_state > threshold) if hasattr(neg_state, "__iter__") else (neg_state > threshold) - if included_pos: - scores[literal] += w - if included_neg: - scores[literal] -= w - - # select top symbols by absolute aggregate score - idx_sorted = np.argsort(-np.abs(scores)) + + if state > threshold: + state_scores[literal] += state + weight_scores[literal] += w + + if neg_state > threshold: + state_scores[literal] -= neg_state + weight_scores[literal] -= w + + # 2. Select top symbols + idx_sorted = np.argsort(-np.abs(state_scores)) top_idx = idx_sorted[:top_n] + labels = [symbol_dict[i] for i in top_idx] - vals = scores[top_idx] - - # Plot horizontal bar chart - plt.figure(figsize=(8, max(4, top_n * 0.35))) - colors = ['tab:green' if v > 0 else 'tab:red' for v in vals] - # reverse for descending plotting top->bottom - plt.barh(range(len(vals)), vals[::-1], color=[c for c in colors[::-1]]) - plt.yticks(range(len(vals)), labels[::-1], fontsize=8) - plt.xlabel('Aggregate clause weight') - plt.title(f'Top {top_n} symbols for class {target_label_of_Y}') - plt.tight_layout() - - out_path = f"plots/class_{target_label_of_Y}_top{top_n}.png" - plt.savefig(out_path) - plt.close() - print(f"Saved plot: {out_path}") + state_vals = state_scores[top_idx] + weight_vals = weight_scores[top_idx] + + bar_positions = range(len(state_vals)) + + # 3. Plot State Scores (Left Subplot of the current row) + ax1 = axes[i, 0] + state_colors = ['tab:green' if v > 0 else '#843d3a' for v in state_vals] + ax1.barh(bar_positions, state_vals[::-1], color=[c for c in state_colors[::-1]]) + + # Set Y-axis labels (features) + ax1.set_yticks(bar_positions) + ax1.set_yticklabels(labels[::-1], fontsize=7) # Smaller font for labels + ax1.set_ylabel(f'Class {target_label_of_Y}\n(Features)', rotation=90, labelpad=5, fontsize=9) # Reduced labelpad + + # Titles + if i == 0: # Only top row gets the full title + ax1.set_title('Aggregate Clause State', fontsize=10) + else: + # For other rows, a simpler label on the Y-axis is enough, or no title + ax1.set_title('') # Clear title for non-top rows + + ax1.axvline(0, color='gray', linewidth=0.8, linestyle='--') + + # 4. Plot Weight Scores (Right Subplot of the current row) + ax2 = axes[i, 1] + weight_colors = ['tab:blue' if v > 0 else '#41729a' for v in weight_vals] + ax2.barh(bar_positions, weight_vals[::-1], color=[c for c in weight_colors[::-1]]) + + # Remove redundant Y-axis labels and ticks from the right subplot + ax2.tick_params(axis='y', left=False, labelleft=False) + + # Titles + if i == 0: # Only top row gets the full title + ax2.set_title('Aggregate Clause Weight ($w$)', fontsize=10) + else: + ax2.set_title('') # Clear title for non-top rows + + ax2.axvline(0, color='gray', linewidth=0.8, linestyle='--') + + # Add X-axis labels only to the bottom row for clarity + if i == num_classes - 1: + ax1.set_xlabel('Score Value', fontsize=9) + ax2.set_xlabel('Score Value', fontsize=9) + else: + # Remove X-axis labels from non-bottom rows + ax1.tick_params(axis='x', labelbottom=False) + ax2.tick_params(axis='x', labelbottom=False) + + # Add a main title for the entire figure + plt.suptitle(f'Top {top_n} Feature Importance Comparison Across All Classes', fontsize=14) + # plt.tight_layout() # We are using specific gridspec_kw margins instead + + out_path = f"plots/all_classes_top{top_n}_comparison_compact.png" + plt.savefig(out_path) + plt.close() + + print(f"Saved combined comparison plot: {out_path}") def default_args(**kwargs): parser = argparse.ArgumentParser() - parser.add_argument("--epochs", default=1, type=int) + parser.add_argument("--epochs", default=40, type=int) parser.add_argument("--number-of-clauses", default=2000, type=int) parser.add_argument("--T", default=10000, type=int) - parser.add_argument("--s", default=10.0, type=float) + parser.add_argument("--s", default=1.0, type=float) parser.add_argument("--number-of-state-bits", default=8, type=int) parser.add_argument("--depth", default=1, type=int) parser.add_argument("--hypervector-size", default=4096, type=int) @@ -168,8 +240,4 @@ def default_args(**kwargs): return args if __name__ == "__main__": - # train - main(default_args()) - - # run just explanation from saved model - # print_clause_explanations() \ No newline at end of file + main(default_args()) \ No newline at end of file diff --git a/plots/all_classes_top15_comparison_compact.png b/plots/all_classes_top15_comparison_compact.png new file mode 100644 index 0000000000000000000000000000000000000000..9f165f62f799ea5ffe518e6253b77a144d918c8a GIT binary patch literal 101867 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

$UA;-+w zN;6VztT<^fu|N){=b?)+B^(5}4LlzdnQN>}0w;Rf55kK5gC7lya1_R&;pVf!8G_-G z=Gc<#J5g!IOro$TqCox0kboh7Fb?^zqp^)WSl zG)F5*s9W4#V&>BvYpdZOt5HX*SGbCT!Nv($${>B9O&;nWUpY4t0|s{kF{f(WZjcG) z9qo6G0iq`D3*{AeHIi`BT95>PK+9_sgxd($3D0t_Am?Z27qSl4*5H6IlG3~sz6jvo zjKbamHgrrcuB;>hEe8@XH9ZE0w4Otl1%#(J%G2GQ14oUW8uaNv2!0X=y8xV=WchSD zG6zWyx74C90)-2S$f|UasA(J;!7e|r=>aP0L+tA?B#mX)0JRdIrcEBYkSr)#QT#P3 zR-uF5$5Ja)_f!Uulm$Sm@U%()E3yzvddbG#p5(?f&KTqg_rBMqJJc0OO{69XDH-0Q`OPb1 zEt71Luo12UAXsY}GsPP9k}#_r+?mSVd*05Cv^`f|ZshIc<}HDgj_{F}=E8-b23Y`? zB+(D7H079>mWAPka=^;nj(^d3)FCIXES4}M{DZ**U1M`^c#>JK>>aHcSr8I{+O-@Q zG3C6F`jg;<$15F@n))xR_lTNC-sQtmk96!a&i zfg&_@lfR4(v$0Qb2T#0Aajrxj;f5YHFg42Xkukn&H+H+@>98wx<3|PmzGp}F-Mcz~ z1#wp#J@XfB?0pNcApsD!z{N_)=6McL|A>;b*?%KWE;2@~9|K+wAy0Ol3a>5e-l6lURp+ zU}OfAG7C4-!tM=r6HVI(Udj{3Cg4nR7sp@%HlisY)(HR(g6PwFY&}-hn)2Ee7vtlL zA49V_0uxD?9A}Qbk>##1HFwH@oCK#CCJ5!__BTL)Ubh6#O0s0hB;3zNZDoelE zO4+t^Jv`@Z;3o44Zn~6}qpw6?18E=(s0%{1os0)}UmcK~io>y%(Hs?Vc1sU4GlUp` zRQf%Sb#Q4#hj8|=w+I2owBrP)k*XCue0|?77e;KYdH-osamQB0rG67C_Ic8oOMmD6kStg_KXUHceg6aEvO5_7 literal 21594 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 GfB0YDU_Iji literal 21627 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 RG2T+B;xfnLkDR^o{{ZRQPg(!~ diff --git a/plots/class_3_top15.png b/plots/class_3_top15.png index d67877960798849291986964ceb0dcde2daf5bc5..658467fbb018d4ddeadc69e350f56e9da3adb3fe 100644 GIT binary patch literal 44941 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

FDg<7!(FV( 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 diff --git a/plots/class_4_top15.png b/plots/class_4_top15.png index c25cec84318b72bfca22cd0a43174d4a8e8f6ab5..8d7e4228c45ead370f6160e0ecc1e86e821fca9e 100644 GIT binary patch literal 44842 zcmeFacUV>D);+q7F}A3Q6$=<57DPdsbTl^HC@39~5=AFJ-{ibXe^r^v7T}5wddb;+&?{O~_ly~grQuyrpj#I8kVEfj7%?>Nxd|kboQ~6P0 zF-CH8*%PYz>gTgB5z|vL7S}aCw$+e3Wa*u%C$mkzFig{3Tn0{0>(BLD^XdAhw&u(l z>k^IOoEnFwA}PVH5~rl`hitm;xE%(AqoAl^*39qf|8)C_ej~YjJ}3QZw`J)b`t|cs zE-VIv@$SySdGyIgdvE^q9e#Da^egB0^cPF^@0mxxa$NoYFYy1_HHh7V)HJckbNje3Kg2S{fYp;ll^n zyr$4E?=P({JeeG=ljq#Ccn;&UL}Sll?AkT6=Pp)OP`KGz7MkSe$2K|9U#5|wf8B#! zqP6GMy8P>Fqx=O8jK>B#I$z!S({yKG?&Q$%V9~R83ww?!D|6}SG|A@mOihkg$EooN z8&!O~o1~y%ihGfjmsdS-;9|b#`lLjytgXNNvRlV#&|BNSWyQvg8#5Br`iAe{zdtb2 z?Ag^BmtW*|A?uoSNezS>- z>)DU11)c>-+E*wz_vQ=-XUS7reB|nAgs|5x&|2+S=Os zT{Q`Mg;%d$T~{^|V3Nr1DcN^9%=Gd3Igw7|Ly_7!4v`9B2U_`DMz4*IwuQ2{6nL%| zv8_LQY5t-`!l@>;J4*tD1ua{P`SaP7)YT0+&VBxE*~*pr@v3oGu3i=3%N;qH@ntgK zEG>{%E7Q-m(Y~O3Cu8^ElN$<*&y2y&YNZ^9!3XC?Emo{t>2tg-&^-4hS4psh+NCAy zZ9c79zP!CP(44jw)^2BmUGw$MhOD&vDJdJ7hkSWeu)5lI@8_^fpHs)@eFjXH)Lj>8 zjQ(TxTw|=G?4d)IzBMik%Z*m;dR1-8JJa3CeD!`x*&a4~*gu-;|jt}aiRV{cAWh?L`2Ztj@F{+^zm6B84KQc3CQ>dgh7YS_O% zd^(Bo@!J|~o18rQnnF6FLY-r^vTQmM(i#;F4Wqd_)=!UIQjJ%?v;W#kqv4(g2ke&R z?Ce@SPR{*L7V<@(czRKm$rLk?Vz{o&kk~MXk)FS6di2P$b?Z!xYZFgrT6LUMR8o?| zHz~0*_3Q5Ix!5J(KK+d$ktUBVT~0HqXw^F2g-v|sFJ5fZd12zC*AaJo zR?On#W&YM$?eqiJ*Z4nqvPUNP!opPnE4OR-Ra7VzB;!w%qU728>kLBubqhQuPb}%` zO~nZ+7aTD-$#W!F+`2egA#7^L#irq(rW!?>&gpWAIdJ3bobJh+p0enYp7{KwpbrIu+OlvGi zaBE4RZE6341q&!N@JLH*-$@%Te*7!?hTSe(0Cj`Br5 z+XwNQI^vRYD`qc|yshIrx*OxE{`XF>-nC^!0pE$M%(r_;xx0cI}_s=Yic)v80WBv9l?ia}WoPOfs4eDF2n;oeE- zgL=RH_6HWBcex3!IrOP6Orj@!Z$;8xBazc@!VyijY~L=2pR85WaZ=&_62$YcPsqGc z)8F5Jl~qI2#rPk-|GqtExUmvjBTTQzhskPg>Wd6Ct9bn9+`;xRPb8APo#kPdI9w*q zG8MCIx~<+lnst5sE^UL-pz`jtro`jsIjQxI-A#Eqh@72>JJ~roE#2J-a~7^Ne13IB z-_TGEG7jGs8^-QxPGGDhHTU%nVOtg^_ck|&A#X;Buia_-3X2(q&&A0F?p&~Np^~bq zj}klbl|UO}r@Vx1k8+BBNulW~fj9%~osE+Lub$=S$I1tbui$O|Y5n>nclRHocAkEH z?>9thI$> zD#lT3K+s3C*GbqMuGO*)Zgd*fI^C43HFxp4S0A2V8|*9Mi!*P^^>8RJFQ3Wr>%=~& zcXICBsfw6xJ@C2VYytb$?c1$0ovo}MOd+LqBHUG!hskVYA`Vnz_YJ<Ch@W}qir`6co?X)JXsF9n2ECn;k&*r>k@VNE3k4kp46VE0s7N^s zyjp9XeFtxJ^7GRr8V?L_5>mrC_7U5>b*pS{Zf?NN(*e#S@8>3{C+|V%w~g)plv36i z<|$o))ITse+|Aj4~LS>rg0sO}RK?16ZfH<7sB}QA&f0RtxN_j5=alfQ1qp(773MVBfaT-}=3lGmB^B1Lc0uratt7^rF*igBBM;Q-+gK*Ew2N?Ugg}m z>>n1b`PkkO3-BU6{z;I1m$N1X3uL=kgig`aWJ&9;nrfHnaUDQBqerf@u6lZ^$;w_W z4Uvj-b!8CnL=;#fdS>SJ-`RVq^YPsERrgLj1wea8v9j8@I#wlafI?wwv43ZMMvA!2 zr)Zan=JgZ_afY;7gK!UqjoJ1Z*>=eq4^3m}xOl5+pN(zq>WZtXQr^hL6^-@7Wqgon z_A)8lYCDhWS+~FX4L7TdbFP}G$<4>7y^--wQJ;U+xSlwbU+-A?p@a0B{}%6t>WCcaX;{EZKPZvw)r+)_o>k`m(Gzd&B*;*m3AQ}0oj%#YN;GO z`c8SMC#%=Etu**eih*Zef4}#wTj4zoS#c-;c#Rj~!bfv%RmHaS{3Q89mGOKR-Wbn}L#@t+7`~Li4 zQ3cNS(f5-J7cSIGsSDbPD>KsBxNu1z^{dyfs~8#@MriU$NE}CPa`qEyH696xtS4hA zQ51jw{kHBkclY>PqV!XC97~@ZI&5qli@Rk%E-&wqwU<`;%#~j`vv(tvtQetXjiAB9>@{0wk#<)c4ld$U*{{8Xyg z!ZsfAX~!C;zw2s18A6~zMN#oCLKhEmGoB>&V>I@u>mivdtBkJ3eGcb)5+r6xsK>gq zikYC1sz`VVRRs?hS9txHNlm;0VS2}I;gS%k<3IoWvqtjC3tSyoc1lwxb#<$gb!DW6 zUh5hICGiB(%T(rx;Bw>DMfG7j_m67^DRs-)*rF#U&ZRA0->_m;??Xp?=(fJtiR5UL zR^GI{W{2-;IQKzHmN;0lyfWNKt&)U%jgYI?Z( zww1P*-tn^uI9!S2-Ftu@sPKtIV%Mv^M5x@>NKX0WR`huhCqwhR-;4Q-qh7(kzlbjS z`nTWGEhpxDvdN-S76}PqS>wu;D{I89qWk*#3KOPZZVrt`y>(wB0oD3|y)RY3Yj*tZ zx^(H%<$wX)N|DM;M|_>@p$ix6`Lr&Y&*QnlV$o6qG0H7iyx52T!1P4hF6)VrvvcOm z@s3%)c5R%PWvk(r56`!5*&^C|zktK>FyLWTMZ_VjJ~yQ#)DS8K9_(@V?Q!MZsV!QH zAHMG1me&BEEBUnJGc4P-ii)aNB$?cyLi6gCE8N3Dz`k)n6n#Uyfx4k_+cs?23yhV* zMN9wybmfx^3whiV?%g}OGqA*n-jVTA#{`Y92iQmV-#zTz6o)5vn4TOa{0wOBG*o9$ zSXdZ)Zz`PWh4P^O} zg~q9>Mu+h|49VG2ZY_p2@%}$7UKf4q*7Ab*yZLO}kKNz53sF9zvFmlR?p9u2A1*w< z9JrF&7dJQX406yNMm&pqP&E_wo-9CRB!f?=Ak$XGs_>+m)+rJ&0DLb@$kQ)bvwz>d zc1q%a^(%h<`OXR+)rk{zSk`Jl$mazGCa>?EAbjxT=~Jy5d0hNi@IJ=Y0Qty2LsL?|mNL#s*I8X1zWx>$>2V5{V1H?#B;;wiyx#<*|p&%o3~a-N}0T zdESq!a~M2oiOMHWp7h_oeS1m69ZDsS=IFj?-{`>^hTlgUSy)MyXx)ZQ^*$PwfTrF3+HM1 z&p*Fco~q58Qo-5qsy3c{+68{0P(MZNG=73{h95=TJ{+?f4Q6Vd98yBg6L#1;+(JUi zg8IcrfpC^DU3yp0pwtiqg@+}@BRVbJZ_{=qIUWuWVxoSEi`_(*GE<8Se?~9eVII#* zR2HdvMJrF&rz>P+WULX?cjw~XwMz$OY&G63!BPrqljFl`V7hY0dn9OI9oaQGVBfU) z-|ZU}8$g6@`h`_R^65X$Kd})!3X2Q(XY}#KO%J9Xkr20U|MI+GIx}Ah6%}%X6(L?g zQxqzcAAp;4$2xh4b}$DnzA&7uk+1+M{|zqd8sK*9fYv3qdrN~v&+dsj66!P1CGrQN zovf@Zi|hHbXIlWQWjxm1FLE_>5)%;4!E4ox@wX)k^y)ub zt<+_^?Z}-{XS(3WAC;Y*rJu^oVyx0r)6CA=%V2EYyltg*N&R!|XbPyVm#%${+>e;r z+9Au=ed^gE`Q7~YbJ{DTJV6Tud(4?T*91r2>?(!kXnR6Rdp}d|{2e7*&tlr?(OD%2*~3HC!C_3S%n`I*6;A2KO`Bq| z5O1>WQ^GzyyRs~^zf__++ujn`S@wwZXn5r$Rl(1m?ZXF+v9rDF*3q?DA5N!bNqq15 zSj5en3o8wpDKd9@$`Qon5h}bm9NIZ$6h|J-UJ`9mn@Btk7ZY#ethmcxr^EU+F0(sT zcjV$Ly@T_3t~@`faQ196>ct3$!A`)e0Fb(!X2zR9qR`t{deg-$-3 z=Y;GYtCN?j?a+afyiu&Q7u7Q|n!`Z*oiEJ=MP+44_2Yk?4ohYrzJXC?dTy`XtkTro zt*GoDpwPz=I6p=;6ceh|WBcn>TGbfHMCmxC*WgRA;LFttBle z5kUR%fNp-9X70;%x-t7$J{u~kPHYr$r?CfIF#z*8bMkwcv?Gr+KixKiM_c>^^sWND zCb+qMZ*OnUy=~iJ)OONsVu%^F#AASOet~R-L`$4yxXz`3($HNxdoTW24l3?csz&Om zmrIW&sGe@fRQVrgQB8=pceIbJzh0HzwtTkco?p&<0hRvU?AhL-p=F;H7?1YX0Wtv> zJ$gC++I}rT+bshaL?SbQfIa54(WAUm z{B4ijAH>evUc!GestnsSdKP(KBh6_^fruL;Ht;-aDZwdq8_FHKQx!k#5BRWE5(*oG zQ7y|o|4xzJC$ER<+X{<{EX7x{vnLD;3@j+)M#i_WxQ{Y?#q#CKkrTAiUm>CJ?~fRD zraNVh+1NaE816{`ffxOEnuI`T4^^4gCQ_dt%@((8J*+46Gksg>OWkEp zuDu6YldM}{j8Lyvi2QR4sH&yN*94K(xIR4@(g7b+AV1n=dMdLm#PNaSuo+RtX-&>q zTrsG%1EifY1q21XZ{ECvIFI0IMGg|0b{9awqxo zzoHCTR&a0v)ow#!VPV!IpHI|v+t9t?V7DQy1|07R&2&b09htP?hjxmwk(` z!liJxu32`1MbSk!ewvb3iMKzQ_I40tc{{Ov4^O|n6%_P|O<*kW?%iMM(2CTb`EzWz zSLb~ycn@ry-q#iu7F(sXahXTKetK2bl0!@p!&kep_?F@}1MLdJrmsKWOaVtD_jIaw`SN9em}R7{3$zLVQw2M_k^Ne|`D@LqY7&ln`}=b<9r{Z|gzY~6&a99V zX$S||u7G6g#YHFtlz}hu5#rYmOE}_h-1rHlO`NR57TUzOB_T>Z(jpQJ;e>~ilgKxa zcaq`b!U^Km9moSO z*}@(@+U*22h!y_efzWoXOy&9W=g(wa0vG1lUk+3Y6Od$Z9dkdiAoP%biM|rbIh1az zr57;LxBOKki@x-#(zD&vo6h?#S;`G3PVI|-Rz0d`(d0T)#ouoe#N%>n0Z;!Y9EfcPruit39+EZ)h zkTN9MXT)Yy_SaB2@7-e;ayKl@J0wKgtPUjLEkD2Z*Z6}KOP7AwqJshoWLgw5U)13I zjYbHE#IJx4qB4eR@z_8*_{t`!2j|U{F~0@wJRQV(4w>el&$LKS9a~sdPv#-4pwgZ_ z=K=SYuUZu~YxWN>z+LRUw77a9U-oTqj_oMYu~jO!r`6vD{hS#iOpoVSXHsopJCEZ} zw|lH$DMwZ0&ybo#tu9ghWc6FVzMDI0g;hFhllbDQH6lNOJ?|SFwC>cRG$MKZgyL#n zPR{sKq%0x2qY4BY`x#3^3=`f$<&|ZdDTj@Yjmfg8ss|_6iBqi#Xsurw6m#$1y#+}+ zp!cC%6y%oZ$5jmBx{T-ylp!nve%R_BPT(&f;Td*RM2OpczFiZ4EG(-x_qdEFhc_4L ztW?7zkP#%18mv-6`Zc&v8!BYPLX>7de(KaI@3i^2=?P^MH`S?-!G!Q<6 zu}XKQ#*g0z#zO)8DB|o#(Uq%L$2`5X1W-h422>c~FTg@yIREFLEp2U4Q1|w*tZ>4e zpbdaZw7xKW?|(o|RH{rPqZlCIC{VAW?B-R`H}y*bB7je{aIQf0Rzp%n7T(CDnkV0V z^#&}`hYu&Ob4Y2ZY1>Cg1kn8{FMQq@&Yg(p_g|I1!@^mN&%b;<9Y^)#)wgv9WL&e= zT=n~lE%bACTgFcE?GBZ8x)mJUZM98Q^tDmRZ}@YyuN;8uRxhM9VbgXX$@6cQ;wY~& z#w`%5yAv`4!mA;KaN{=*V#bELyAtn}8hryK&z(C*eAt#8CQ=x%!`r>j6?=Gj%ZQ&@ue4gfrM52ZHR&O z?3{={nu>nPKi~vjdromaExZwoGs;~>|7N5LRFoEb=_oVa+1Imv!aXXZ;L`J$22rkh zPST~D4i`i1%%MFauXQ~;Kzu5LUvlRspt?yuC>qqI)R$+=c&|J@nY@LMudGAe%E~I^ zN~bxcKJc`!UL7U+fO{I$_6nT5*RRzVvF+HsGf5FU4UNucm**?$D7w`NlDOW)y*vuq-j(`q| zxMvERw{44m!iF57qORWldOBg%Qha|j_qP0Wfc^J<9D9)M7Oj`oG$;%4<^n-I%sTkP z^g*2T03o9fuRT3HYTiG+q?h(KxDyJJY;ShIhi=02Uf{Y?o*@Lao&dJbiEhtE7Ua4mWCGuZ&ML{ej1e~nC$Si zX6e5HMEVYxcy{0(dRZX*l7Fd0v6D|0>2h)6gsGc=-|U3&I(la2_& zv@kTJ3zsgL;El_-Y1!22bvEW?adkkBQ^XB|c#K9w@Xi1+?_F)26U|0%@10A=M$U=T zAN*iH5c4>;ZRKj+Q_1SeAI^Io^z!l&29C>tmdFZ6P}u?y{{H>@Rn|EWSQJS20VJc{ z3(Qk`Eq(-Z_7h_>>vc%*4Mne z#jPX+?FJut0lD(Ti4&l6zpM}Oy>a6<$SPn?us9@rX{4XI1EF@XRHBz`E%)Dc)A4|l zg^x*^8TdAD{0Md^QxS1=5CG-H$z;Q+iP1Fzr!JQUiEV%j2SnXUqM1g z3^zKK$^ES2&anpMG6?#fopwd+dj@9%Kh4=Mm+38(EPdfAVzp96B# ztFPFbe)9Q(C?{QaA{;>3$07Ih%$v!1!V0oW?i8hO2t1UiF4qXXUI?{D;(_6M|ImoiY_cgdoPvWSvs*P1lP2&li;e51% z)*~^vc?|+X&5~UcGr~OzVTUizR}qryZpcytFxcL2ikoE|QBsWaA!5O7wSb0(M&|=y z?RGGM6k%3yaP)ZGC@uQ@{wX1(Ry&~EfnvcjL-;b@K4os9kFqV7N2avsFTG70GO0=QVLm*UblN`e`4UIAEW$b|w0M?$3& zpr>ST-YrxDb59EnZC0;vr&)b8R;}F!)p&c?5pZ6A{PBm!b^(FDGPi}euSb94a0=zW zP%OQ47hVOAJGXp))pbYZ90RRGs_(@Hy+VY#1jn8Xna0Ld(;fQ%iV&rt^H<{v(Kb{n zXo|-iK6BMzDZzMI^XA`jo>Kd?mdvnvtq0NyhGNl8fnsn#vt zYMBDzvVxFriH!h<>zkBh?%xMY4w+mP$<6FC9BGVnt6Qv}GTjd?dqQ$0tLlM~l66PL zek7{I?8(p?7|q~lILM*{gB(eEGpYR#j4BDQA~`X~Qm=!(undxb$P9TkyjXbiVZYm} z1@+-UQiFdYn7AH7XsD^q$jx9dyKUF_^V0=g;f08ej(*XhzI(T8;N6UY*QPt?{O|(+ zTWH%44A;3|t8~|`mQPM9)D~-HH)pyYUm3XzXFA00!~E?U4-TUA;!)C8RQzx;8jr%u z{&z6&;E>wNf3kEVkb}o+!2>v3)v!545Cla5a?yFLV{=K6m=btY8<~TPoyGhKzD~k&iVB!s zy_wRXP1G5|15Qcu%aGblGFV{X@zx1QBrr4@j`p>9=puP5LX$-8Pz}BTISa;zoTbN! zG1t`C)}yt$EDX0Wp@Xxb0;n$p?gHs0T_VVGjr2LYjQP6%GguT3Sn2EOb-A%oc$AAudz)r0>7kH5XrSd#ioQkHHilw{dfqV@<8$c#1*gIR-NW7n3x0{j!j5 z#HK@daw5qD3f2l6)4~@fH^J2jZdYvR(4j+v{}Sg3pU1ElRb^!h#dNo9+jgr&-|{Po z{7n0=0LYxT8K@G-uLJbFjfQSGf|mopVExaE5dd-Jzwg?Fg1Py-6QfzmojS%vDd)H4>fTPAJ!2Fw0 z<3ZE0*}KnM_uFVQXp)!%;~18O8$vM4>f?)_iBczPVtj_7dwG3^mnF0o6& zguEY(S{zz(0_Q#z9zcNtdPcB5FBknhX%BjbY#a|v>ja`s+{0FX1<2>^?HvJH-MXh9 zcAF9lu0L7iQhZ^S)&+xxtxP*ExjXfPibf}Nxr?YALY;<=H6S42F+&0DVkSqi(Ij{XGA8=v=;@G1EGr3&c>m2cz<@Y*`CX}&)Q38k%WiwR^KJ7 zbHKQ->uM*W0NG{+*XCeBGX=KM&sWcb;^^@;{?{5!1I$>obW;G-{O)Q_!EU9p@g6v1eOjakhk%|SD(CIfqT26!2tnktPHZqVX;DiIl3Z4D1ui;#O z32O$KlimU&27yRVmx;YhCX?4_MvEvr(5ZEWJf;wQqIAir@N~(pcqzvr9LB@&7DoK= zBijsAVNp~p5W~i+B*PgzMyq5fi#=LqpCREIkO;mxpDkF@UXcYFBns4k%;@x{8^D!^ z@6V=>>HWV@#N^u=EX$-ktQV^#YTB1Nf}Dhj{8h4fO5N5xPF&1&ZS_{}v&6!1Jdw44 zG)0xhS`jm45G7RIchqX<#VAD?19uY#f}oT@200|WvpwhMEWG9A6-E>Zdl9@Gr=_( z!(?!H&r~}P%macw7C{xf1Z3#|-N;w)qr)fJ1$;*wAPxmYixnV7va=0^M3g2!zYv0<~l{3x<*p|x4_VDgKIEw0$`T7mEtKp=@SN!c~idN0; zwjqN5;Jh*=D3KMFl||bnV629@IviwjEFceAr4jiyGT$WYUP7{DKaLNgqFmRua^=d- zJC`^V0nJIg0TWm>CxI>>%lGt#g75paPh^k66`Y`*qcJr##R{+0anT~fy7XB41`?dP zZ=P$vbLrG*==u?_BhuX0p5Mh`LAgcfJaO@QTqs&q*7$FZaH*ZWy%_5^U3bVJN)^L_ z$aS5>mBt^pgF}KiI})qHoYmD|i}j+i|A*R(*TES7UIwd6VKSes1I*)$E{E ze7HlU93O)FSvL+TcJ2}mWt{LqV1Z6@eA9=>R`8#69`iU|t?C@ER*brp#33NEV2|~p zXJ)z|$Td-rjI#rUsRK3ZEiR~eloRkWWG>XEeM{gyy9BZLBE;f#Vix<5=+AtLiiqIm z6$)i4g`{nT3wZwm!xBU( zCGi2FJXOR33BVRt?D<2;pK~Y~V8-Jdsgm1p?VFJh! z^E4#`OCo{lL01T#L*cG@OY5w={GLIJe-JT)xN!ytue7BvbmX9KTt0j3W`RWt`}D~) zN*DF8hxHuVUlaB%2_4sCpHQ;Kb^a?}Q3uxa zhQAFERDP0vghP)b98{P)HD*Hf1sRsDYqgNn#+Wnp=zl^=mtf(J>D4eHBB20ymfD-+ z-WbuQq+pMHe&4d`G{6dJ&BA(x%Zc$K=M5OI?M!ka&NMVM<{XFWs0Ip~kgw=4AWAj$ zL4e9kk6wYR?H<&BzjhC}?ra9_-KQ2cdtv#%hI+g@$I+Hq0c1FkrhwT5tgIQl~5KEuP-T=>9^|TFw2xlzL%WDZV#fB~2imeexDQ>N?I3-^snj zCpKP&jtf?YeSGtg5ZDHC*NB*X65R=44@#TBc;DEvU1#E|x5ibEMSUOu&bWefRG#bP z2x5pl0<3V<)3I>1qLt!dxQ^Fjh~`FE2+{-$qUi_T?OpD^ zVlC)5XDmLw=I0iwYB*u{{n95*DB%AwLP+E;`5s?zG!-n=SIgx@Fq}qUBsmwmf-eLzEvkE1p= zFfjdw0%yDsAr!Qhjd5_M!3^Qy6j(B3u!kbcyT@=+D4-}x1;Y)hayL+R-JIa#{}Li? z@ghbQT*&l2Wp2bpF<5K@8Zlt26G$Ba4H_VN_Je(>fqOaf4BCf22~|Cige4zJy{#!c zxGp=1H@dAsFyqdpdn|-rqZcDIG<&l~cOD*1nc9lMv{E&pTI znSu0AFZL{cx&ap6|9~JQ&P86v7q|u#;GeHM;y=KLi%`Gx)+MArx?XBtyGL0Bj-VNM zk%LG52X#=J>%{v1=cT(#@am3fGJ6sVL#8G_=8Q?r36e3jEW(`|hd*}$|EE$;R%ynT zKixim_v3o$?0Pp6RUKOW4af~ZtqmYDX53!TcuBhiuvJ2NP6Zhv!eIocTl@#e!jXQq znW`wjybjpEDiMD)>jiM>+hfrxU#*wqWdaMh#wUeC_V3s`1oRY+BC%jOcuX^`rzu?J)F-n`sO`G5EXzk|Wvno;Yy_fTog2u_~^Wcx`0ajz5JoPi_V1SUerI zr*^=hJNt(pj{=BLgf{{ojmQUYaWqtS@@O&hV4+3Ro07n&<2PutKPNE%99M8dqQ%-u_$ zzZ>YRK7>fc3dh$FF94ys60JB?=z&-VvX^Itqed`BCZXc?e^ENgK!<)4snxn9i{qTV zJei!HaGky-%l>UQ1!ML9asm9$^Y*U-FyqohBJ~CzNw53&7wx@ZUSx3mQ^VvBPO1*K|A-{4Fq2eEISv03N3> zQ!Ec6*in#W)LlV!(6&VsSs$$!x%m0l@bGXF_>oJ!aQAxd<-5?XNPQq^#MA5CMGo+~ z`w>38TEq;*fX1XFOQJV|ek=%Vs+u4yC1!SRrG^ZgJzFPY85nlUQQJH)Z<5}wovn`5 zA>%X`csjDLuL$wN;Uw=p>m$T%039P_b=qpwhBru=9@74?Qhxo+Z&OGh<;+KW`A-de zM+_(oJPQ^rlIpcYiccbL5rC}_9oGh-PLVjoGNV8X)sb>*VTw6Qr5}#t@%O3UfTB@@ z04m9qC8TY#zM2<#KciPE7MwVU0TNHG(3Mm8c6_=wsJkNKn$4%zcY=b}QIU=QyQI$= z-{hS{L&8_@1U70zI)CDo`>pA}UwShPuN14GreZU1M6rZlyklyi>Jc{qx$EqOS`@SY zdPl==xxLb}t5_8cwu%xuRVFD{*XTZg?~Iz7T0$%VM*y%6u~6zol8*`$9V;A-N^t1P zvZz@F{Oms90hK25-!nKonR>j`6(Drt$@kP1(Ad~WrFWWHLlrbdi!O7R=WudaX=!PA z4HTFlBpzZ_R8-vZ@!7O~{d&?~2;)+VrP@x)=dPFTv#rd@3Wr4Z7j6DN46AVW)75LX zw)0%QB$-+^2LBUDolL0BL?uG+Mo$)TE=0yh)^F?InMJjuc!+C7F>F;KpN#BhE0u?1U=?A>Yn@+MYntcME z->}iC0PZ7&ZFGFR2+>6%XxDSdi2?$O02*KAL}8Ayw}W=AiO>6Owuhvde96}GH0aR) zD?}zq01X%zKK7h37IHvi5Ud(sJBhsE#B!a!W6c?o*7c=W>snj4hNOAz2m36JviekY z&wHI;-2OW=?@K+Asxe5z0*q5LC6;?W>bH>!$)NB1>!+LjvsRY9wFaj^Pa?T|rg-}g zCoVnyx3erR8{dI;h5J+ll!xY)NE!zEqbzP2=;>za1oa4aDXRIDa`KAFi2QPS+=nSm@M?p9*r(qP8|HMhr)E03H-0+#T`G zWT!(p(2e>OsL0CJPS|T!>>VgHFVJA4q^x|(oxc^L$^lm0!_$F|v*YeKPiHrs>zAW0 zbr5gt)?fupke}Zt{$vt_g0>DRw<@{JJllA3iXLJO?AXFkDO-G)0wlZ{NeXN2FLQ(n zm0wg;1OnsP*-vO86aY;vpJ%U`jgadH`lzAdMAbn!CN9{2evE1oASn`ps6H$fX6~{2 zx-YiG2BXQQ3$&1uFR#|e=H_PQ8qm-bzSXp>y@@P{oM+J=?L81A8tT z>w-OnXH5wk#v3kCT4e>4D^k=wLtSaoqaT+M{DK*7JzufQlZ=pIm{NnZxwyoJJkgL_ zA!>4$>I!6Fsvnx!^1j3>HTJiP^f-_n4U=s+_M_3eM}Mplvmm_F+H=pC%ehGf?ks(+aT==xpL6DgG$dEJ*TTu^cN9+_sB5bQ{(xt1$0r!aG^_~Ll?iQMVep`#)=&R$@Z;ghVo zyZ_qO%cX*DAmfn7Tc&yi^BQ9J?zz0R5(yn;?Yq0B==bpsx15UDj@E-2!^~trv_R-E zbcq=V1_0~PyFd*MCV0VHL`BmrzDu0N`_X?IQW-gR2-1+;S=8k@*YNXNTRd4{)5!X9 z!RbH?X%M}rd=CqZOe?qc=k^uy2t(tTM7OucvDI`*S62r5pzbU7C@h=wclmQ*zzS6` z|H9VLk6-QbCRhS&$Ix({CI#|E!Vvl*=W+G#TyAHwlJcfB5DTsopraNK*bu4yqJS;o zFt-g>8yy`T99<@hFd^FB+TnFRYtZf+-f!PX_DFI8pquMDyM!u9?~(Gsv2^xJ%5?&0 z*H9OkaYgtQnhXPPuXv&J__fV>UkWXy@ql(Mg{7r6XmOyEguZ@=0f$)BQHprj0o38^ zJT+=S3Ld1f8tN4pGCVq=qDv7P13HhQLW_v9-ItV_K_iem4uc_fqwWe7}%kmva zZz9MbB8W5j#;F1D!3drkInxi9ym)7Eph(%Xf`UL4P62oamd{tH`8~h3DvQ7KQ0=Is zxvu-m7cX{DC_|S}>-{2LK~n#fp84kDO|N0LnB~0}(Y8mPSo;*2khI4Z>0UhP8eR8M zYzsLc_!vyV_|o4mh_fpU26qQuM9-p$rGqkMZ`pLQGmGKAK83xk3;TXa)6|fN5`a(c zZIc^lNA$z9_=65q%Cb4VZ2fw3?Q>uDTMm6XW_w9h_i*6Ol70L4ug1Hbxodb`3mZ7A z4Oi;=?I%3Iko-Bt{l9?$kyif=!~T~kStDLxS&2v`57MJK8_@K;Bn5r^q}*XFjfL$N zmHe;DzPH78aC20pKtv_J5PfM?u;7r3JKs+>;}TC0lpp|a;AVWfk z;VQA3;aVVS-xp4=>Sb}U5F{l5z{wL-jjN_vQTph4Kfn?@`@t=&H2zr1 zVf5poruFE+!L&zAl!GHZYyuc*=4G4bO+LKh;jwciu|vfGjE6zq(_A3zV;W<$34=d6 zrjR~G;BnajX+sc}0DYvUNNg34K5*fIfG+_^ZGqUCl}Mf`$Z0b6pc~w|3Tncc%Vg?c z{El1f2{wJ8CbicTNR0okHQz;%5qcF5UA$*7+LnN$Sn|OG-?!H~h#%73ku6ANn zxK^`ypx)VWzbLZSYS>hbz~<9Z7{T0U52F`*8yyC{QVCdv`Sbm7IX}1$%V{hW_<#VC zuAn-ovdv~Z8e|h#z~DFq-x5*7*H&&P--8h(6#1tdOJL%L2S){&nhG)eDDAVtjx}T_ z0zEPKVuL63c7b-ysOpYQ#mBh&08|^ibxGwytGXz~De_B?VJ> zLDpqb!|fpD%S(eL$a{%*msJ0HBQ-J+67I?FFST$~=&Fv}Y~I^9_YH)#nW02vWG_LB zlFqp=7XSmUSakUmn5D(RCYgzeS>(W^HbeOOy`P`Vh<ph9nPQR}8l&27JN16$reM0`wv?ih4>8^iFW8eP>Km zki)#Wi`JYXcZG5rWV3CRd)u3vbul|-RY5YOU)sSGxS}i(<1H`|>#(jnKZ*(gvZE^5 z!}(b>5ILEI{L$RoYYt1ai86F3821DX`(2N5(<)|V@V;t%fWL0T{aX|qAGBt`ejHhWQU<8S8jQP|vH+~dUs%Xu%f`{oP zIzLdnq#^vN_Ba(TgPscUELEb>!;o)l!YD2|n zSf73t&HTIl?-SW5Rs%eSD{u=6_GjyQ5w+W3QM8@9Z0V$;7s&~@1{O{Q)S+l!gA|%1 zFy8=eo|RKLl4M?+B)31@2_ep7Mlh7f!YQ8n?%_m!LBY)Q6*AZm)LFLa@GI(RJsuYhhwaxzigKpM#oU@CbWS-mC%en__CUb z(f(BYn4I5^-vIV(Gi@M6(gI;sI=YhbYG{ZN92TMtSal5y(kMqcXN56_%!-sU={TBs zggrPmHBQitm%7l%DaYe(|KZt{tM2YJb?d?JkH4e#L7J)rKJFIU2gxGbCS+1`7#SCO z(9CS2?V9OF0rJkoTH;Bz!pcgJcBr>03Fe2rGfy;uDqdDj4kTwFHKsdFjkdkXvQ5H> zIKIs7umL7ew-lfs4G_X0f-0gtV(_iRNlB(nPYvghyKNnl9ub911Y|c1Wz(WM6iB;G zplloujJ`-8xLISd7o$>yn`3Bo{C8W7?q48;Tff#If0%SVSt-C_A7QuJO*xP$W zViIeKz93w2GdKXdmLCbG6?s4iEV73<6~zR62;-3Jm?ZoT^u2|hLjs|AJTuX8=gyjK z+S%#Fr?$YRwu4X?*iQLFViP#dJ3%nb73!aNP>iv(ZDreq`FgEcL2yL2fK@81s$OUmz$&kG!~%|-~^1a^=B302aEgG9@J z?!vY>j8l9V%^6XRS{f|&~KH4lt8%H z5Fy!@ty*P3T{?DVI8G^S{hM&pY<4K zi1QvyL^gb3G^PU1-I37TVTgHES+?fHUyyMEAdmW;d?f-mF&HELG?drH5d0yP(efC* z(0FJc%>sqsYoC!~;BX&=x)HjZXy*^SF-bB)`sU4X!6qji78m8lG}nmX9~jEJih(7C zUtpm~@6KxI5mh~U^cV1ORKv@w z_z#FQSb4$Sf+-$7aGyQW#ek>Xiz?tCLBbQwnZU3WZS>5{3}gc=D)@rHvlWJMFn6Qt zwc)E#!{553KiH?Tg_pMmxIGDd{Zvmu=%>_%IyelX{?;6Gy`s`m`0|s9e#Gcg>Ylor zsH9*@!I2mV>@FJPMWYI65Fw`PfTwX87rdjEN!@oX!{0N`9Ki5n;e&W7=sZpY>KF4M z$38YNO$Y({)e&Py0?G>4S+$nBEosqlnxe7rV{N;pJCtirCW~(To$4Koq@lK%!QLhv zs_8PGGFKrCqv8gzG5-ASu{f$Mt{HjP1@q7--D0XP{)gTSy;68>dsAJeopWgCB0Qk` z>u#b}mhls}`ShP*jYuoec@r&s3+B(ii9Rb-g||_$yiu5f!qEYY?~5)5aG(nMHHwgH zsOb=%2KpqB!xkoXt!GuDmIPVfNt1>i7dE$sEvzlnMH^rOIEE}qXcj(j^GPD5uS^C-15;N_tLt{}$%RB$l8mUQjH>v8C7M6B>|83#jT2*53G4MR*6 z;NTtrBlHkbixW*cVJf1S$+Yd|#SA)(siE0MF0a@6hlC^&nn0+XnMS%W{q*C7=(McH*>uTI@W7)Ho6HECrX5oYcIP&5GTBL1L6pPXI9jKpM2WI{20kXfzh z*Kf_6o=n1IwjDqWg5$%zB1D5ybq)D68wDmf8>%r$+pC+D??itst*NP-O~YJf#rs#`4)K3^B4q&FW7ejG{R#4E{8jq1OdQN#boN3>dzJ z*{O83Amer?CIjtW)Q>?uYf++u?lFZS;mgp_&b*1fYg9bT0js0j0>hAhPyoJ0A;e5! zoJ(u2BYA*>{4S$KI!MjSp8WRb5B}JSV%2#ty%lk}QN@nAQfEzz{rDr1Ogtt{5;O88 z(Q)MRnnt`4&9w%5t`dQWX5acj`;{ofOiv@Iha~{Qg;IH__VwYukuRXi*%F2iiJ^`D zK*AE$9q0kGhvg&K12E?ma_U-L_Ziv)t01SKP`>`*V>VqVo2?JdjE7$79)`r>&Xt0D zvaA_*+M^x<@%R;H%D!Uj?|L%8TIaHEL<;v(uzqD}0s8CY02XyaA^z2)jh=DzDe*0c zGPme$(1}CArA94nCBn6$xf?W*X3#8#G2P_w>7d4047gMQiC^##8Gga$pgOoSNJ5AL z@b;zVHz<7)Lr@90DQ3g#Xtd+Ef#@ zC|$frR8e$DN-99s705dhKR-?qBnKl41)X&aElkJ8ZY(p_Dw;EZJ!Xoui1dUUZGR%f?u(8mL z#ZXJuU3k>mukq!+LPgFiuFBZmf(iDnYa)>$Awa%D1x4C|mSu@BD;%^g$$2<%Rrqrn zwZR;-Ua80&!;IluAt61lZF=fE2H4*}{|x2zO`7^PNXt3VCRnF982@0}x(f(sh4^oR z2+Rt{%b17}b!l#EYkQ!B*|iXY#H4&t*wN%#qWuv3QxQ%me8YL*LH%yvm{eZ63FUcq z52JsLm$^!^zY`-jqS4N+iAXtyId13!Oaxs^(<(j0p;8ett^(%|XFv=(XFPfpaMbOc za4c|;eRD@XETn;9(6D%hwTU<(bPec)l1ifH;X2VXzxy9MGmDY<8|Ad4&<|i<5=S~K z?bx4%k(4tu5Mn`rn*c!eE%Z53o3XG->jO-n@o}6);3kxOR(xoDvj07}cersDN|&J> z977dR2e7!Hfe_<_b_2dNXN=?AY5Mw|l_|WtFkQ5W^;ROo0N`a6TN+_bT6VVcWZo-E zmRQlTf?ZR0a2&wY$w84Uuup3_sXLxS1}i)f_*>%)rtN;g`(~KM1H4OaIT}+!&A)Gp zeDOQ9Ad|d^r>Vh;^=>hNv5DAlDk5kc9_B6_@5w=jITOCC2JXi`abipN;5152jWj#Jh(tr@sObzC@y)#xf1)AS z3J1j}V~ScV@K!s})<*NSW+tHkMCQEH`yPDpAmT0$DiKThKrI@My(CJ%Ze6TW6FCW=dusx08e4!poYI|hn zCC(Bvso6FNLlK~34cG!IMX3n{({#vX97Y5t!9#*CG&vnPXPZbqQYws=-eMdpR@Xq1AIb)JgbP$$5nl{~XNJ|{kNgxa zkk2NFTbLQ*jlJfFTG{{a@@$myIsAHER-xDo4MIj0&yw?SN-Ht#0vRG$z$;;5lZZ$y zRWc9E8s1dJsg;e4kAneuo^(G6eX2d=71P9(~k5{_NQ^Ho)sj z4EI81^^!b-^VUh~4nd0NbLXYtr$t`}Ww)mjJ8+^H9%D zpE$Y^4nUgc*R#V|2-8gcU@o9PLH`X*Pcs7$x$v=fM^Gp$fqFwx)Z25^Sdu>a{Qs)% zOTco@+qQ3GUj}0tYm8BrkfKPUCd4h3Xt7i#SyD-;R8kp{CB;p)q>y%H3zZZ`A=18C z64EM58>RZr^B*&w=Y78Sd*1i>-tRa*$ILOKy8r#}|NZ-2zw0`$^E|JW8>P3SoCcN! z7VS^2%Ln=Bo(Uh;eiQjA`r{Ce|8l+g2X4vtEhMP#Dl03$W9gJ!TdJv7*EVEDe;k03 z@2|1a%vs;NwEZ(F{szmv8kzl!=r)f(U+1-e5F~qZgkAqWxA*;i?Fk})GOiGYh&g}3 zf^4J+zDJK5*)*fzpG~I)_!Pq--TwhfQTB-{Q2-d>L?w?oTo48<-0%B9uJ86^;D?mv+nl~OSZ8K+%NFBa0^^!)H%I(KrN5zSzSB;fIN_YO;Rh?P z(weV}5@va4GhcTi8C!l;Z6BlcyMz|1ug4bSJTt*E2W!-3Z9!Kf{_SzH$2@WoWp;u< zs6kdYe@7ejliR%ie?t4D;sK2PRLdAkpc$nm^#C`m;H5;~C$b9gffmUKf=apmrYoOo z8JXGIbOfOg2J;))6vA4Ylw5{`G=!@QW(%Q74%wvHF7)vkPNS z9LZ}q$)GrO4W{(Mt%hE8`7>w|Zrp4d?j!_@->Uj)?DwS4|E8m@WM$)dV+f;$BH^Ns z1UlOhUre!Q^N_Yg1C#`dpaPW^{QeDh){XvK^0Kr(Do2JK_8zr zyP7B&L2i4x`fUfim+&DQd?7^fF;hssa4$vXpM6e}&Xs;v-!d0J=vr!FHbLo_%O7xb zdND*pbn5MAm$q-DynZwPkYL=LbReVq1(d~9-xi=Gdn&~UCNh?X;PEAoIH(Jd02l!y zd@YsN5ns`t>cSnYdUDH%?pg8O+N6#8<3R6Z;{$)feShsV34X)HPJ0FCPv@N_$fU3= zH!e`T3{m*p0zisVtC6xTi`}&Pcnzfa;}91?cs2U17Ozh_cW3vAvr!uVo_zPN-e_c$ zM3*oTpz7srhCath&RrswAT9Dz5Fn+m-kv%obkZj^35+1M8-UpU;`)mQP;d*@Y%drA z)|F+$_6IbTjf&bw?J#qPi?A+OMxYzqRrFm;g(#UFC1oG zqD|>y*JneYBV zX=`u@7ZzQ6-$(0Qe7FC^);2{;0v|{6EocCWh9|^D)Ppkj22e+KBY2V5WN*MN=(|w7 z^J5#e_W!Q2t;?(>U12u>wx2v$;&`rnibyJbrTIU)GwKdQ7q0U8)2Dmrkpf;M8}->x zdeMn=*D55-t2gxd+pc++Pj0`*o#UqS3153Kq1N!u{S>O>ox5zWc=L5xzc8HXep5F%9L9((4cHonaB2fuG39V6 zw~JeW%o(14q4+_d7B?peKJ3PeTnq#<^N!8q#3+cKgSp9ymSb}`0b~Z?W$&Q(+dB_i zh&5LS?SohXBGgMek_(Wbyso%_Rqt+@vKxxiov`l%DHJ^NN^}r-i8fQ`5&vkqr8a(i z)tPEco%*KBZQ8t@)cKqR3hVZ!z7HBF#+l4nFX5<1iKrr<;?xLnCK4Z_-=L6`A+aU* zqApVlV^qf8-{^|2GX&l_*hZWNgDk#8Fx(=Ts#VC?6}aZrklWpDqdHC^cY_WL=y@~( zGvlwagcwZFzyQp#LS|kFWu-vpLWfsYuEpNE_CL5pFWA{2*48!v$;UkP{{670np__= zEiSQ058RHf_Zzz^q=9fwFxy!=9+uGvLwYbKkr6{)kci?u5XPKLd_dk>jPgQ8OlTfZ zAG>d2xcMIztl1$xR4omf1R$RbSSAo6;~p@Al*aToWQN|&e;aA_Kh+mGKEfr9cqG)G zM*ydwJP6PZbSQy1=(HqLHay3~1VAG8=rd9YFQFp^=<*?_gK%;8kc*Sx;qm@QvuXd* zN%OIj4v^0}a85$e9|JZ`k42oG6U*~X2SbaMhib(REjjqv(kC1DjkA`vC9qH62ZX#2 zFj7wcN5Xk>ZV?pjgZ_m&IQx+5@Um*A^e{me@};yky1wz=Q;)Z7;foDMPc=GW{NamK zs48@}!rh?rp8~A5WQna{$tl+#-{4ZQ7rZ6Z_d7(DJYN_qQ~|avpS-2yUM#<-I6CTf zn}u|^Z=cM5M~lT=jG=~A!c2%=DWg1agn;v?lY&r2Z>ZvF$YMBC1yo=ZJP!`Leff|= zh7h71u<2j?9=#dE-UmR4oHKI-PzbonA?dgI7iR(P2CRCL^L{!Hb5sYw8Rj*L3nyus zleLDU0vrVt9jcZDIh%Hm(iBQT^C1bsGYtbN4Iw>TeizbxNHjvIjR+NwU%6 zYWP_W#zaU=mUvxKLP9_C=})~LYauO%o&`WXly%vF&)Mk*k&kXZuq#vJ;n${;JSw=+ zlhzNo(n|rFlWdv{|L@%H^i5tdp;4FI9eAhT%Ak@pMNf&P~>*g=ydGGT% zbO0N6^l)x!AE`;sauAXqcvk&oq%{mCDV%FlvuUdaysPf=0t0}g4vq2@hInW!@kSz* z%W(+cfku4Q)U*{j?w)5@jlWnaAKPuAeK+x9-mA{R zaFrP|5}sZH2s{QY*o*uE1~cm2M83%s0R11G?zvi-APwdKZ{l767n~F@gQ--Vhvx46 zbm3Fx&xAt^x=UoMsyu@&E+Xp?47qg$s!d z(A3e*h_==?!l0QKrkZUU*e>G=kbChoU5CWO+q^BnNTiWG;aElGb!059H-Jd`I}dP+ z{#&+Y_?cQm2qU3_9pMETsjUpZqb5#FG{yN6b8?V2%UK0KouFr%LcG?B@2x+jO_wj@&0@ zrI$i=&mRUn{>%EY$MBAre{!mq-IfmO`+b*}o!@oHr{xvKIjYpZ4)df(#0&%(MQv(=dVTcH0(GI(2lw z<`r>!m-o5Nb~AxTI@E*g8y@sJ&t4M;mm)%nr5|_`?v}eZe0Coka^b*suc()>4n-56 zLxJFAtQ&^>(fbumP>|6){YeN&jzh0Wd*CMiO?O+D8(J$b{Ot^HVq={6w=cZ^&jGEE z&~%AJ{~uJXV2U)?u=gKjKd7F#Zl&7NOdCdj%5KR>8gJ4_@8sE_4cVEo_M{)>QN#Z0cDVcXHeax9NoPCIp^9*!hPKEOvh0IgLQT$IM)siU~DPU7Y8YYv6Rla`^ZoL zybf(bB|+tMl|yNw&B<>rwxe>8SNz*9wtN3n@rZi)(IXwz6e;ZpZZcw^qdaO&q%?Pq z|5?k|BtA5B9_gr5zkyYx)u{9vNb8sB;|{o?EMxBQ9J+!dYHo@?_}x*yS~$u>3!Z-b zu~_iPHI+B|hYWjw|4Gg7@IxHDsZW~n^kNa2|8}F-52=xhubM=)h6JbY)G!(~E^9@s z{mY@=eP8hr58H3HxoApTu^k5Qy{yo!rsiIkhZylKLpkk+1eDTCpZK?MQiv}Du ztG&#|jA6)-Ej=P$?W#o^&=Q)QuktzwUsNPXzA5q$Hqb&Fc^Bo-Z5BmeJWJW#Lmdx! z8Bj^mkw7CHgT5GtI#YiG(kHV@_>~DNTLy2uL^*dw6*Sf)GoTPArTd?!PUOn2SHmqW zH2EC<<#EWu&loXrRJV9>hV8wgFYjKDSDyM8h{<#R#c?5^iDZ|Wp6;hwkc^xhZhx~& zI7w5at_@Re4!ER97apr|=yz{EQiMK_+}zUjc0Sy;^2!N`zwB{DAdKKd_)g; z0TxaKoe0mILpb7ES8Qz46(lej=|?rgqh< zx1o{I)GZKjC@Yd$tm`k8NYr(FJb8D=nerFR*!eUd1u;`JP_lDEx;(@VwD;pmLS6u zL;}IVQ=DDEm*BKbU^MzBCg*9Lwg{Kv+)Q*}@0HtygMo=M{}i^(6;MQB?|VBgd3*cL zwJ6ZafK@LBO~|z&zGxkS0d;Pae3Nn(l6V?7qu$-nT=ztu1J<8|e6*d1_VmPM6d^&H z(OjG5;E^p&ZXe*^=$=w4j=r6t-~xI??{8_I|H$}3)rSJ;Ehf*c%Gn0C{S~BF7)CTt z0##WZ`WVI>kx^90@GO#OcQxC#EkWv9a|XUUWWqa)IxvzPJ8~o#T`$FDQ?aPk^CSN{ub?S5vPyo~M<^~@gj=ThaxiI3_qOgdFED~RjA1`>3 zk4?wtggL&AhojZddPMVl*>V?`FFsva2U%im|OhMW2MR<`RH>y>9%d?0yGK__SFVswF|2k9mj`N1~iHz zX_3cAbxNtMsIcB$u^R+ih+48Epxs`E@_Y(iGejMZB)@r`RBooFaSR$joPi1NZ+CCZ ziFQ4Qw3s6y1)pea+vysY#xKA6Ft>@H?DnXuM^=yia`%YGdu*a?uve9+R{?q%S9K|s zT46d0gC-bY4{3PePd_7CERPA?a#nQG*gxfnqRcM^my%M@ZudQQY)tvRrawT9JR;3g zq4iUWT9x36E&9QGLRT}0@UCtsk`wp?Frph@e(#;32C5z z5WoNlS89aPeN08F15G9ZFpy~p+CC>#?89-I<6Q(zYl{t+D{u>CU?b+j9kt578dX!I zTOwQpkyktKYoRZqd)5T~1+uRuUQImj1k(dPSujj4PCtAIZn+z-+~CEMDLuKFv2mV+Vp15wPuKW(JT6X!dTtcyh6P|FZ)Fb@COgX2PH-APr{Ek0 zP6hWhb%VZjub0=AtEkok?y})`r9<7Od?}5j3o;`Rf`V~iQ{m@0L8Sm%^X3meZdXQ1 z2f*+avUD^gaxpYQMhus#+GI&&f!MrJ=cv9$@NN9MoW|;=f77KO|bPV*O#)gpnScYK^LF@L@dVh~VJl zUAtXxpuiAtF~Tlu$33lbc%s0{n%Mc6cy>bGjNP zFdSi9L&~xcr@rOF7SK6qwLfKcwjyB7Y={CU3Lyx?U}eQ1&cuKsfUB>ZFmfX}VM?Fl z@aa+#bY5=`uKCM>ouO&fy>YKTUNgZQB(w{nSXg7DGm%EZjP$Su<+cE`)JAH}I(ZM! z-!qHPfxqoowIC~ZJ_d7HFRVYB_~o3ql<{i&3;D}ljr&)429e4)$ebh#Vl-67(MW3U zFsIqT({phO+&5<%MBRNdT;Ha;YA0YzNA2C)EoMWJDOCp`{J}yzxKsCpC+nc32VJdB zb{{%WK=A0k?LK_{iBy1@igOKo72^}0>2>IUFr6~f@H514mMCRgdK3qN1ij{~1@3^W zX)80c8>;auMIF2k^nN~~c0i(QW>3#qJw4Cn&P){r6t&=eE3Tn5($~ z^B}Cu>abtdK^#JBC8Gp%bUsZ68w95nTyYzHR;ZPO8|U=42pMq84Pk)PWRdWVzPFJ$ zHq|)H@Ci=RwUm&6%oL*6aWyE9ac|aTrAb zJ}UvSGW+L<57rZ0S|JlT`FlUTD%ST`maY*SFcBq z35gL*auc2US#IglZih$uT(lI&i}^9qV+f`V(8y0*5rqU0#3U^V$)@+-I#}T`{1~MG zJ_^7POqe%GN$|-kC!~@`7LF+750CWD;Smw`n2jNx`+SfwCiN&Sw!pcA4?=(G(y`)Y z@eb!Lt*rXP&I$zK&>J_N#-$-~Axw`%?o(o0c{`m>Jcwq_Wi47TTNG3Vt72d0MBktqr{&8p+Gs{`0hswv1-uRmam9^@4zK;MhN znyh$oEt-=F)voZ_Tf}-TIr?OUqjFwn)@6_&EdzU_o?1`N>;J$$?x7ox>SGY$~$Oh9s5Z|ef{b#R#U z@rI-+NHhFeE|iZ}`@Dw*GZ3Yh9u3D(%i=RSKVjy~7CS`(r0SwyM%13>yTa+h89?B~ z7rb!9T|lk5;cZ@St|iuH9P%yjBAd0`KHVA4#s&qwDG4dy2*2JG8+kr5vg*neUM{J? z$0I!jpp;#T)Rr<`x5h(8pF#boLl3Kx0ox!KC@u23avULtrNM;LCF>a=nD^FgAz5K? z#*St`LfKClV-h#a>#{BI(xpW?IXSs4Oq4MQ&!7}qy$s5Vv4tYWqp?9NccE`JFY`JC z&Dd6{6E_METyF+q;*W4mom4V=#xV5oI+CYy%^cbHZ#6oFD=!27Hg?XJpHv$ZIQ~ z;x+{SP@G-)%Me4p)d_yH#`{)J#Fjn)PImp6A0QOm|z1HcX^OUChWKD zO@2OpGRf6GXsqiU6_1(eSib20-6<@B*aSZV{^=@T#Qm_y$OW8Sw=g|yi)~2O*pRHx zXTRFs{-hBw5P-g=r6tbPU&Jg`>=E@taTZ9trFWnjT#Q2!gc^3kbI z%$O#q_r`+We`nyGkZqkd$3H_hd^oja5zZUh-ilryh#-R=05~Zup14o&`u;%hr|xak2n)SG?v6a$q5(#|kaFtzGv(}adlb29Xz-m)AqO9C z4gq`&RMem`M+rS_VORmBgA%4k;XL<$z6)0sO?Ybd z+`48+AC2MA0mjV4IC1bBBG49uo8$?&Q`ox*%ly#S0_dvEvv7zi_0eF0P`=*$H7=o^ zYX15HPnU|f?csz73Hnj#K@8(1wqLIG4G9UE>THUZ2$!yv8vH%6_NEml{EdLPEhyui z{4lWV>0YpoAS{WBm`(Sh$jzvK6+y0By=Kj+3(wKLAj@=k-%)(Qyq5>{0_f^wpr>~l zw0=v}P}(ye4?N2Tj*3cQrCwI@0yGfhj;0nOGjX-f$BWeW-42@6a;M8_A1)m4>D81f zu+E!WeiGFHDigBYyLD69=v^&98QXr6#mmxvg}r|(FthgfiSVeXtMvn5Hud`T>rwuG z7?y!0#F^UA4u*`IjURvEb!+_DmOG>0wr7_`-t3QxiV9fWb{t?#*?Nx3Of;qOoCQwN zc@#5r|NYnNb%WM)T+jW8JJPZgGcN^)&f6E4I>f{2f9%oJb2u1;IaVQ-%YqP8CusVP zp8a<1yEi}+es5b;$KG6;{zG6%Xt-p}wQJM&x~n6e!nkk|qJq&F>!8cvoPs3;_8 zF~uX@AbB7yRM`N-T8jaZm}Up;m`E)iDUpP z;_JemuOjIo2TeXTBRCY!fQrN32BgKLT%^rtFebG$^I*zcl&=-O3x<6cvmMJ>jQ*a@&#@cRb;kgt5QDES|=c&5JV8*AM#maT7*J&6e0@%o$LQEtChM zzdekc7>lUxvfmGlTo?p6DG8og<%hG%RTBi`+upqgj;KBOckPXLdieoFiQLC%e6_iB z(p(E`L=uELG@=?OYb4sJmauqe5o~WQe!H!2&P?E^+iM2G>F06V`EkGRyy)FxdE|WbM8sn= zK$P%Oa0V7gV`*$7C5)YJZm0zCkxj!N2!{zMvG+A+fJqP>)DWy?G1v}eVJ=wjb%z4J z%!W$SzjX+A)-QnvdQZv1XIYDq7(@ttq#9A(DGgd9@=jpgIZH42iE5|8>Va);>`}&X5fOVx) zB97BH0%<2Ea@|C!6Zs1Jh?A#j-NPxEz{gS&kPcmjjAPw;=zY|Jr{S{d`}zhiYl{W0 z?~%w^;U(-yxk5ihH{otwKfilX1^g-;R0<|Mv=jkO<-PShgx>c`HZ5Ha1WWA%E+zV; zEhQ8?Qx}tDM&2l*hW5a0sRO{rPxEmBY+aCFNsFmOMAZucmb5q%buO0u zL^0hjp1U@!$Eb&Z79kht;s?i|tpp>klvXa(C=)9cCXhh*pBM=Nlp<*4x z^-9>M`byJLgfvqy#u(OMnD`8i1TBJSz+F@#Ki+wZ=3nX5%MW@Y zekPB;n`fQa*IeAV(!>yHsVVy47w-F(RvV4`Km307fc7LtITKI&hpV6h+i%Gp`YN| zE4=Zf6s#DMJg52V#Lv35^pEd;LF3_8`P4CdMTaq(FoD3f#F@=ar0xz~EB?#ciD{>( z9lV2;=YT;39QsC_!`pDEWh(#ZE|(6Aws3UAx8^iqQwUStkh)`ld0t;#)r=R*XlcgW z8w86mGIVghf63`_MSk+yv}|q`Iv&Z&hG(b%^Sf0Ka|x#$0GRsN7w8|sO<^jMR{xe4 z8NVu>(*;gpiN>{MR`7mWE1--5MB|)uTDRB{-NeCP7M#F58YBPUFLE8cPe6H(FOuel z+-n^`ItNcXL%8VaWVEgPXpX(1#E^4IaNOuZUf;SlXJh;`+H}$N62qu}qq_;}?_dt| z4F^UxxcTLP#B>l8V>Z6hWQQ>h_e?*Rf{gL`Rj40H)MF17X(~wgILge{(^baa8Xa=F zkpf8z1|yd4Y;n!?PwL>g5b&lfqmO^2E(-Z|czdKN&|;E|S0pSPAY|YY+eA0fn_yE6=|{xT`#Sbi*E^cG7W6vbc!pup5Ab44ExsSa6IQIr5I@PcIj2zA>{7*x@nyrYv2$&X-*$8rn4$%n7)?$F4l{u4>K0 z$tg3Q3-^kSmLeUDVpbR#tz}&ZS?y|b{k2oE?uN5zwr9`9dPKnpdaH~d|4=sd-o13J zF#{8m^*Lf)-G(?Fcs9^MAs}$0Cd>un`$4OPE@=f2kAt6YNiNlnRZM}-ngjhP5$6RC zJb9Z7sgn(t$ZYbykfVhGns$dgEN9)vM1Ty)sz3w7kwUKSKqzK!}>f$%CBN3VbjE z2S6hpF|?l2;C|`?Hjvmoomt`#PwxrP6brpCWN1i5Ck~zAa7Lwe?7T&YrHu{mqQR#; z4?Pwgu;$F!=?XJlEz3gOqJ&0uSRG1@HERHZOqpX}Z;suVJge7??6M#!wWr?Ar>_9F zXs!)W%OUZE6tv)b8H@bq2dEq*B?k;ro>kP`&v>8Ip6_4RynA})r#RhqDV93JF6IZ91`GZ?uS%iH_(<3?1Z#zxAx1}my zkCrD!1sTnNgil>a%Xp<+qjXJI$q0ED+fu;krunD6swax2xhbvB&8o#9L$!PeCsRC&0*a7uxO4eTvCRP1eF)EH0|(syj@Vh7w}BS zK0^n3Mw#n5pUveXAqLP3?korQ+?nZmXzznL5)R*Q0j}z5KF~9Ej&OY5@a~yYXHlqUSc* z3tx`S1erDn>pwzGsbtHDU&JW<$WDaqve~0kCP*sj7Jye@oqGN9@n@F{MQg)bzm%#4 zqlvTT&)q)>0%A~fE?hm!k_dg_-(Bk>RvbrX`{5156$G9sZ_rb`QW0sIQ0U>|ag64R z*3WMg&;n?bqGGmn;g$Q|Yi_MUVlPi)4=|&rqV?xpfZ4*rzc}3C&Vf4aI8CYf<1WZI z1fxn2^%WRg?Ds4IdG2h(tt_H#{i<$sI}Ip(VBA%(jM+&2QoSW{GU|wr;d#Ql1FL|FYTh1AHb^7jiL-fN&5qayJX%HV zb}ZHWKc2Dt!S9c6{{36t@_k|b_aWWkBmlt*uI%YOo+w^xzS(jEVeTAoW%%K|KAc8J z)+ku|7OwpuQvU@fp!?(`#7XR`NT{|8r5>y+uJkwyAemfy%)7(6%RUL5OEjV0d0#L? z=Nj}Cq=m=C#26Mpkkt>q()gXuITJ8-^-gg*h3!FB6H#E&sCwd^^|@P=VKK@4f5eF5 z!_{>d&~AtWA0V2_xteyOR^}9I-O-k&Okc2NKn6`^yY@vwtu-YSIbEZhlw0#ap{$5{Fj@5e{SN#TZ0Xh@>56xm(wdZ0MZd(R#{ zz@CrIZT8|2BD)MZvRa81N01CKw%u;JtfH{0+PVags|1f0L}>zb6wINj&E;ti8-wc^n|1mX z=>gJqh66*w15|Gm-H9{d@k-aMa6AbB5&mEx(aHM$qINE`=@+y0IMi zszhJKgiMNev~NBRtMx&XndbuzV?FdW&O2z0Zt3a6Qyv_w3YbLH%O=8wjcw0cgEJ4SYt^%+{6_8A){98O)=D2olYMBz^OX3aJufFG zACohQJBAita)c>#{Z0Vn9wCMDlE#$w%(6P2a}$yW*1!(H5AWy1i4%i)PbeWt19->+ zd@>42m}SvsQ1TGj!U>mv;a3yK78Ro@XoJ9CW&dcOT--fXG@JID*38CIeYF1Cv)n)z zWS_Zp93+92+#uWp?W)iYM=aP{gB7s6Zr{Frx}T1>7o8aOXBy_9*sdgD0=tNqKPied zJ)uN^F%xH#9&&|FJ%?KO_l0KzQ{u$2_TG)f_a5Qa?a)r9skx~={e@uap;TG8Vq2Xr z2f)#wjR>03N7%uIV{WNpVLi9)o6aLQJ=!Syp_$&BY{FwV$^z#6W~EYAR<_t?pWW+> zG_F|yn|k*4G5C~EgBWfCyI%4@I2Hv?1F{MVdY~sWfqh=pq*yn7fsRgnp&Fue9XM(J zwU?*mBAkG25Yee*?%Xa7=m8+2Zo|35fP!{-9f>GN{!QOn0@Du~`A+%=k&YC1-(5=!%wt%x=Nisg!DDMKSeoR7}XMbQq({%v91VYO7+ z*kCUkAzHI}k85j5P#FY)cf>T9y9+*R(nYRoQ^>I^+@d^G+cl`(6Dc98f@guMh@=S5 z#E6lFE<(&BX&9({E{tfAlgl7D<>2nFNkah^RiGdewd?~_7`PK}UEt85VC7)nLV$*R zSIy|*8h|u_+_4T?(j}#&LcaV_tR0?pjO-qrki|$);d$$q=TO^xA=r2;>Xky+nYlJS zDY8U92gQ-m4v$|jP64z=MEr{&%+L=9`INJE*{1A_h78xxu^+^9`ykMCBY(pRRblQ; z#CgEmWHHiS2uWn_z3Q+lC86+O3u{xwn5Js?0K^JoohU?t4GC(FvJKO6S~f-|-@KDt z3+xt7OmlX8MAH}dOhY{OY=TCP^uw(0SKv;< z;bfD-j026^%*uRV@!TPh6u^*gBly z4Ga)S5XP1a9W;3j=TRc*;4%W6t0Oe~1re@`Umgk~3j?(=?UNu}sig}!Tty@>&{1Lh z=WgQ-V@ZSo=>ynl`Ejk2>fEELpWY?3Cfc;A{(_@!Dy!mNGinbQAnXkG>lbwM z=5q%1QPBDVl{$f-fT>?*kj@oET1{MW^C=TgWq+MRCr@(f%zo)Z0RbDl6xN`({067< z6hx&dl84Cr{9Ia-F-zNzKJTo=J$CLq&VD^zhE8~u=3hNEex|wC##m$e-Lq)t397I*xPOsDP8NM&AEImf!=>d@V3CZODS5Gzmn=U5A2x>(i|w z(TQZUhhuMU?z&@zyv2xXVxlFZ%0sNUW|VaP0%_#Ri-uW?b>r#N~c5X70;!W-%wjN#m&tV z`QE#+0b6lHO>(_Oum&dfNR({#X#V#D(CBE56Ri`h5sy_0?SIp@FU+}|g8mcm-!`o6c!Ip!E+zSresPOV$RxQ0TZ ztdltXvjT;(YzKw1bls{I_(>uEHa@%v*_=3QqiCUTW3OwaN0HXGu{5=?F*Q2B!%ol2 z+Q`EEAQ#U8EN|&tCm^KRW4IreA%WPUK_ixaj;OwZT-t zb@oe`MI@LfS+f6_G(B>l$OKr(^eA-}I^7?gjPXfiG9N#n>)?oUK-K=H$ znDfqLgK`xYm+WK5kLx8jjP~g)WWKO6$h)kP!X)fa(KR=lF*)97F@~S3GYQ#Lm?Y*z zsOq?;Mr7G2@8jfD8u?U)kEIw^hWHPx_lUi%BW+KixESFkn1fqgMx$j&HP8kHds`S549eHQJdzNEP_Eiy!d)(8fyk-9UXPA!I z#q|tm@<+%7#%$axQ)44Y(R_uWbE_K2oiX<=4|bo=5S{K0wVH_^dc8L|J=l%UxTZU# z(;}mDvzB!k|K`!pT|GnX1+?sKy)q9EJzlYn(IGyEqwHF2+suJe85$x3$G>#>{r0No}mRR?(YT zB*nZrlRYp}IzRv;>sOQZ;lugB!`7c@y{0YMYQL_eQ<0Oq-CLiSXxIPVs44v-jg{*8 z{+U^9qeWhhW)6${to-_G$0u6d^THN1EWRYV&z*Vw`t|fHZnwvUUfWDQ7T;lKXIGGr zxV}f)KkQRkK&*V|$>}l+w@@05wq{1j$K;u9pGvb;>Hgt~3DwY(UVP{BDX#NVT?|Zv z1I=0V3=EOJ`!!;;bDY+!Utbq!-x3`X5+d1`V%Dfq9U*;}mEWXJN+3YGTzJHD#=+P) z=BHh|^wJt@Gi^=0Sj4oiT)Dz*HriJ&D=#lUJzUCN8!5}eZ&)GjK3TxR^{(C`Z%)O& z>Ebniev^plvA$UAuDAZYdL_&IvL{-c8F?@Krk-ZLKdU)mJ(EzkxBDzLVXzCF}i zv}&DT(<2WL3cqp9X$)?zVQ~4_PywCE%ilI+ekt4*tGSZ$wrJxI6fu*+E$g-kNSV|p zD4#zc@=zvHCGPBFtPD-D*`Z&x9omo$%%nR9lzTaCHAN%~*m3s@$Q;ctK?*4 zZnZdeF-a_}mA7woO`9L~%ZqySNL<>V?_#~{!rVlsRYr*jlRppjDw)u|!7d674Gq&X ztqTc4w!Kj*@#;cOqftSQpSF@2@E5cQdY@{dI-pY+YgLvzKW)Y^OY=7Nl9|+ zuZ6yFI%x2=Ccq*$diNP$!|~4z#x+rQGVS`=C7eDzFu3>dVcajvR`#al&BbH6g*PM{ zm|&XUC+Ys$TNB-3zE@04C#}iq=0Ns{Otb4mvvFm}iEyz1?62;^Z5B$16Zh`j3wvhP z*gG?rSC9V_@o&k#vH(F@FGl_<%v{V#FGeHmNq-^R7)>s}Am@P$cAaa9dGqSWudP?E zPtY3soLpXFp2l(Hh{iSn)2Pv=u9D*73T*BOi`=QJCx$68MU?#V;>j-^Wf|^cIWq%U zCut8vT{BN*+}U-CF`!KUX>xM&4C_`2eT0(%*O~GDgV<*Wcb)W14|C%)eOJNC%BtkR zDI(H5J6)eAV3Gahs@l^F*ByJKBc*;_u}%t`?R8mM&bh2JzMMR?HxbeS?E8~V>bZ8k zeiJ0pNL@(MEs$z_W+tts_JF7GeyT}5qr@I|_L7G}wk2BTqurH-`pFM8%rjnvx|Iis zU{9L~k1LP)uVWEys)^UAcflgSO-&36dTkRFaGf5#IC!Knl3a!A*^p@)d*=c#FE5va z7CW!md%j(-w;#58nKOum7J(S?DI&m}*Wm5Li-Ybx;eMX1cYS?7wbZ9s=MGSy=v9{MIwL$DoYPXc<)Hn5=C0SDo3rY3247y^*!ezlVSYv$&-un%Y~Ii_ z`n|0~`xS-~ue2D%HC@E*vX8ItdhskwSf@YiTThXCzH})0nP~%WZGx5tBIoG41w^&i zTS7Za{hU7!HY7(njdr)sZ)aqTK{(RlP>D(uzw z4f>Mu^1QaZduJBXg4%zj6Rqf}j^yC1j#k|Js<5!PKdsenkYnGzis8;ue*HK1t*T!R zWOOlmyN`K$dHL`Zip2Ld%{e+cvhUwN_Rjr@iQT6+TjMn|6bi1?c76vx(w6yAX2%5ji6(D&)MW3<18Xd2X%~!8dQ-9BW;eR~2k=2qh_igf4~Jgn|a= zj+}`WMK!gC6EiLeVskyRLYURv()CL{N=R~y>u*exjn_z1!Muo!2fK3AEl!Rq-|`rw z455o3F5lXI_(QT=i)Fz&Y~k7%2^OunH*Vfkw6c17>Cz>enf^4Bfu@YkI{hXL zDA|n8E?c*5#TTU%-eS^7C>zN;5IpyE+mAmgn7gZ~sb!=xFl6bUy0yLX`o_IA8P;7^ zoj?8blah)`M0R%enxe)8ts^4Hc209+_4J!Ig^{S%>8F$1HtoIku(Qb9!86U7wzarT ztBC?`w=ZjJ<25Ei`Ue`4Ptmwc{9EdE+~YUwI$7LqS=`#HgRN{Wd-uqZw=tU6kXfSb z2U{``ylX>FtZjDe3d9zhx2-%tyL&gZE67=fjg3v&$#E$~li-(xA9wED$-Zx2`ndy~Q9#o_g8NKZ`*}f8cLq^1DwBiZ1sHv008U(wdxlJGA(^{(d>opa1S4C-M*BT(Q9I}NB7DG)nKX9Z`~TbFxw?2DmIZJyVvD0!XJV`?RbA9 zi7>r$6Rq{9SX{%z7N*K+A2%>_*)frjY};37ggIaYV6glA?lGRosN&I41{Wtm+g_!) z4-9#7d2Vy}@H7t}Zl4$oJ#6*yH!CYEuBq#5Jc0$6uJQ;@6)<~mxG+D*&c!wU&fU)V z%whx>Y{|*e+}7$k5y;4A_(;FZe<~q#x-~CEb!daeBHhG1apD z*u)p)Ys2E(JDo85sTQrGX77?7zldaMv3z1uhm1qAXcoe@>Hs#La=dzKiHO-@9UGgJ zAg5k7tT&my`owxVvDrte+K!#H8K?0sS2rD*K%wv)=Lz2|mYCkpHBXT_wH0y@2+mGT zO_6NF>^7;pj!8&q*~;}w=gytG(#dVdWLz5)zGT^oZVUIBfTvHN-lNe%kn;_Jw0y>Q z3JR(sff={v<{(i;5`cmTNDEuZ;v$DP#M<20PEoo{S=wgMU#DSF`{1C#Xw?~R$ItGF zITw-}Y)bd1IeadE^QHz6qbYb~czCqMc@QbpJZAKIet3)_d;L?r(ow8Ul9F@JHHV(^ z_H|y`ZX>iix`{6itAm|`;|%UU-MBs>{>WfjE# zjh4I5D+)GQ{yIB1$6?yoY2~9~{UZ|-#?4rHTC#2J)9mNPKc|eBq=g+5DbxPuH zo&EWYXhJsKZStb*?2^EK;|}3*th@K_)fv?s%v;EPX3@$eLH0daoQR%hXjj*5`{cIH zG*LST=|zx$RjWhW-#^v6SyffF>Bk?fDo^B-6sMr5$bbIjD%}U6>QXO_PXY72Z?#I_Qv9Kg!T}7v)D2;TLt77PZ8SP)Yn$^WU*drSh_wnO}W|xsN zMgi0N1X0w~C}}zOr`Ap{_vOx=3^7k{Utv4gq9q|AfjJDsUP`g)Q7~(K7XLyh$7xLA z+_^_T{`eyw!DglcWx-;3nugIOwhrSB2C3%FLhH8j{%rO>rLw)?nvcJKOk3NzW`t3n z@vIQ40Yi!oz6aT7QcsJHB1Yw(Sjt67YMDxG=P$euh&@ug%lm#^yhZ?!*ZriV10#s7 zj@it$_ZM;$>}Dswd_u6{)z#H4G*1ga^3Bj$?aZ@!_3DB8s-V2Gz$5iq<|X&o3VUoA zuNCM58&OH_#2(SG9%_4jGl*L1yI&uBVGkc)&AWcP>TE~rgErlA-t}!keHczY+8Yvh ztcuv3YkM11Uj;41wBFD*ab~MGB_G5kA`p5aj{dqL0`T)wa=B=1lB_`=5^j;_Ho> zC%Bl|`;wf-jI9xm0?4>!+V=UA*UHdS0fV_S1GK_(-zT;&mlF}7a%%5iMNwr7D2qTH zK({SzN2|-I|m1K3UWxrogl;IcLrH(V6fh= z7ax8rlLdUf*8{(z)Y@c}GM>FmzUcpM_5NQd{Qvo*g&a%5u$56llEPm}=^kn(REkPx z&z{wt3P0l)bIP0fX=6W$Q$b0HY$t+#I{M~+?T+Iy|Hn6j?RldHv_ix`5 zWk-4a_N~gvlUEV1>9=gzCxOEHhPQWOiVQ+Fs{{fvo4qM8BcqsGHfywQ!PVaCNbgVO z!HML)qoboU!76OpuwkbJo)rJl2ZbW+?cTk6pq$IEZ+HbES@KO{M3$Riv#wdYRuMIW z;B+Xnh*Nj)!fa$oNr}zJ;tlaf00}u1EiVDVZLEnXJJ|u&o2cKR;^{<9PTzZ{knY6oz7VD%o!5&5T^}M zlaWdh#fggUk-K|kRpjOGIG2`|CR=SG7h;#|XFJ=RWnYg9-Upk9yf5qc(W6I6lAjq_ zMkoBJxcIb$f}vp)rq?;9zPmD%pIYd-rZJ;pl#^V6KlHt0gRM@C$$ zL3UCoqV3fbT3}#2wqEC_PwWQ{$kBk$klNiF%m^4lR#zmo`9Rj7ih9B~o8f6W;w~ls zh>OrSo1tQNZE10_oRpNO8=!(QK0GkzJTXwqk(eCCGb$&sb_a>qRv7{zf_R08sOQ$es1pa zOD)Ur@$?^pWuEr}WZMn4#Lv#U@B>-bO_M7%zj6HISygm73!4<$OO%-WNhh-R@29 zEji9D51Sv8A(-rmxkGu1QhmZgNX-6>tZd@-#Wy9bQcNOmR@(aYRg)jYC;t3bIpcp` z!+)cjMd@~ghlfkb$f!6tq=PN|Xd~nURtTiono^d-RzIgT+XeidF?O@Ec7t%Bg&~IZ zf(w#I!oep%(;Q#|^-+JE+-cixToIG-KHD)FD?8P?ONPW9kQfRGX;(TG6%;mY-W-9L zY1s8P5a{{#r?-I|64OB7j}bz;V-s_$z|@kGRAi#bI+uAdC~#5cS_@hLZc_nqM=+iz{nu!_P1<9kLV$gH#WOF%s3I7L61SK0ZD@ zApV3b+dP&oUCL+MTZ8KeJ3hT}<3^K)wsw$D0lALclHe7$krYfO2AV;}Bw7Umk9%!B zcoJcoFckb&A5X_?WvS3cdt(!g!KTKjCI*qZ2s<|2JFwh5t+chZl}gr|T(S$}UuB3j zeJK{L+AMC9k6C+AL#RJFe+9GJRHTxi$wu|`Y|S zQ^}7@i#!m&QB~NB;bi@?Dv&YsDAn%Y-$KY{Z{gwJSbH8A1XU7tAhBHp^Nsr1MPw0U zXrDh=c;A2EGCjCOBv(BB{aefNMVb+Rwp9)XzO~YDa&nTkAu-6~#QjU;y^l)1zP802 z{jbR?f4xP17jwQiQrPzIR|d`VqM(4Do_=>^b#z!5EB)rp+DR=4?PSRk<}inQ_wExi zk<~d$U3FwmzF#{9EVl1Pt z_k)K8*Hltgri^Q|zSd7>6BV7mH1_GugHY6S(O}l;uhL04O*GrngMSJiBCxovq^+8Q z(tR`df3|)5qDpMX2byESM4afWi&w;ehj)_CzkK;p(%bLeJrtEM=NdY#lr!kaW4UzP zP`_;MS($#ZgU|)h06;#zjmQI{bEAq_tPxeb2le0FE@-#30QAo7CcDn#Oj|Yo4s7RM z&#0nq5hw`% z0nx^=E&mtgft5EaFbp2Ou^7Z)+2;Umib#flK7^D4qBX?WV@_V{rKW+H1mAQ9KxoKH zmWBhHiFQ6(;uNy_$3X-7EHY`O#B#>1iXQ zXwZ7}%*+YUNe&0f03SyuB_+KWKI6xw3|Z?!S|i!RYf4Eahxk?j8oPN0qqg^2!OqBP zHmr3NWX_>8cn}Kbhc2&^KW^Npj9TE$8>!_hR+MZBJ#lk$G}IFeeP_uvtfc+>r3vk6 z_xbFVZ!wI>_yo8V0z(Be*Wp_E=WExKrSNBwy{9JnF00XN+HQL0;DZLY$gCx3Wog^J z+sDQx50PvpxwJDg&W_xYgKTQ#IkSeRCqM$MS+i#Nq_A!XyjIZOa_rc#>?$#G1;4?6TF9NZ=)kXKWMn|W3m|&{9HbKDN5?B> z(%+z(`eqW4EH~5!U8>D-w(oDr(Evqf{657f^}SVw=3jvrW?3~5y*7REX~rO&GUuoJ zN>ic6;lJ2g7IH@|%KdwHAWnJ7b|SKFGRO^a2Ga^crk z)ph?#Vf_CX!T-bifz_Kzo<1Zrw9`fiAwQfjj;?VSw2gbDV*qof>%i>$uEkT`;PT%u=hV=|1BN@z3Y0# z7BpEHQt;H2Pr2CoYXx&%9Yx-KNbUpWJv}{{*nLV_c4pY{KSM*Vy0m;PyYMOOC#2yb zLZm#>S3crl`t4?7mAJWizXHCWXxXiE1NK-|0jz9bl!4tZ`k8T=yVj;)8E*Se<~QHibyKqQnbxe)w#2nnJBY-Q=A^08qcI; zB9ohZgMtQP70G+jU%KCwRi9;gk zZ^}^ANHec}bo5uyOmezY_wH>1C^152f1hC;31dMR^f5^q(GDR`^j=}m9#C$TujZu{R zWP`ui0x?#B$RO&-Q^pNX!$Hg>fI$QI`k7WP@@;u+Mm{@~K$xLF$NzJ*zXSSoPGkn# z6ejsRY(T7M*$=9?xw-lDg+)Xdg8_)q%6di!6^P}eF!ysY&uZp!cX#(DnNz%M*oe~e z?n&r}4qRiNu(JwY;9XJ2+H zCK1J9|s#sCGVXIO=UC{)m$QcKo1LV0oH)-4qPA!7G9#q9VqjleIA7=g64 zH|RMwZQmY;dPIkgT++kuohMI3#1Y-)ex#h5k=WG7jYJIjz3RF5;6YE{PI8+=KZ@xD@8IE) zNBvmx=FK0d9ykO>EV%?1Ny!_ir$^^!2K(MTI7qBAsCi?F?uN=x_1LjXzT7%wL80(= zxV({EQ&j+^AF5E$hx-s@kw|E)Am}G2CnfvdnF>I(iA53u@lXZP7&E~sAt^0gODH{D zMuOIC?$>Odm=s8({fKL(i)CqYS*#-~EnjcAXH(pMjc0IhNueHBP9RbOv2WmK#L`8- zVZ&pjeF+-WL$Yz#Z9gO@6RC)FR{>I&!_?rL0M9`sVaReS0n@vdfs53FHf{M!`djmK zhz4~J@u#N9pgzQOz^z5?b=Q6QD@+bC=tf{s`vAcj@JdO}3L6Q@q~ zmHex!SLN(H#2i$j_mIzOVSOT~Wq=9^U4aOkkB%-wHJq*44CONxShTm| z@vjk54?cJe5Ll4(Td*Lb#9tDi_c&Nkd?CQXxUi3#TNd=XA{;ukkB_fRHLjH;t|-9T zRF^3$Zzoi#)qpT5)S~qS(ZbUJirVk$_gg=B>5};ydIDJR7klaZEeG=Fx^Cb8fzUm$ ztB{+#^y{zmh;8^|HAbvvs9^X_-^tPV%$p98^791+a~XoIHDTaaYTH016<-?@z<=uh zuA#f1_9m*QNf_nc)1Y3J_AbKx!RX2p z-8!ygK5ioct@~hkno}y!AfhaS>4dMJUsvroiISR`#{IS0x;Xb3RHC<7&C+Vq6{u}KbH3_dY=;h=C3uKzL8N22`6Ov@7Dhr%1z)F*=!OU$)=&@rMiyxe zFCugY)zoiM6{_^nzeqn2>c)s21Ep@R;gN3`B<&uuSv*SZU+q7plWzH#EH#LA#Kkj> zYHAdYoHH~qvpeV7>D=Xz(YW8g1!=9Ur1Y@`RFg}yEqPJ~`TlQ{O-r{o@m%P?*+Q&y z5RU6mC#2f-t4mY?_QjBb#~39JF*l(Cj)wD<{uwo`5z_MD6S4)O)IehVRi&O*|0ht4 z#)_&vJuO*Q66sC3`RnrjO*P_Q(W@ZBR4gr%=Eu|WjNU#xOf0_0EEW<~acapa7duWO z)o_5BMa^Hm%Uk$^mpMsaQM&)Avi;U`7C+C|Tdd8^f3MBCzSiadLcWa%dakyTcn?MO z(EmHg%%@H*+u;dP4XMMCurYp0Jzr_*fZ#;g+xPEBK|Isb*C&El;TCXMrvb>|^WCTF zhJ696@(t8yp-3CCa1j7W74VIm{EcYf1e)%dd2c=C+2>}+9< zYh=+qS}MA3#~-?}PNxtmVq!#`n;xr8(mg)ctD0BU73}VY?@Ru-v5|eVtsj{GXlTAZ zzP`rbK5tyVp7FP#`WO7D#FfrL><>~?LFNT!gi)iefKJS%l1hd-!9~<{ZJke8X6zWb zdChi;XFi`{BcC?d|PISG4LL zNJTWnAr4M=7zCQ_8qEk@4a)A`?c0wafn|EjLL)?L1R;X>^h!4A*e`}AhACkW);)WS z5qnS$)F9rb!n6+`4Ox%$zOoV$@@LQ9C!R2bYyghqgu!7o0>n{II`<3tIXno&kAR|V zY;3WS#NqmXxV#-5weey)q5=qYkCiwy*KX#lfOpL0@b_dkv_Dku-J$W!l-vnSt1+5g zJ(0mGc6QI8!bT&ZaQyVsF_4mXSs^gTfV>!Z3YV9%xw-k3enfRJgpQ`!9L|&xqNx@Q5yLG*A>}s`5n~^88`@tH3-~p z?8S~k#=}-&bqQJ=5^L72Bc|3SJ!>ngO^l4{e`lPCZ8=9SrWAmSfg2!g4So@MGJ%SVFV4Fl+7SM&~*|9 zE911&qC%bhQs0w@zj-xBx+WLoXKdszd;~Y9OV!S4yCRhl2s2czylcsYhNpVqQ#zlG zj*j|w6F(B==7Ikqp65RTTeRJ#^pJo`QAxuL2}8(vpIc<3rw=!P8OZd4k)M!kXbbqm zOH>njiVkksT+Jykk}?F~d?iLBK<;FEV~}7AIZXpA2|bdSO`|~$F$ml7{GGZ!a2sFa z5lqze@mTYf|7ZAeWZMDO9spUW!!|y*0u%o*NW`uQCAZvm=I_9K@wT};tR$VlBSlbv z+eB8Yqi}nA#S`nYKNh${ygQjIu5$W}Rj%mcS+oX&F(E|}7Pl(C$z=HBYYdK+1quxn zTM{e8cgHY^XW9=YAdGW}iM4K<`%>}DOoJHg;kZu^qtc+u{F^Al&&Q{8egsTWihda* zGTB&25xF7FshAw-U!Z&V@VvM(lw?_e7-FJY@%*5%cJ4gN1>-}PmE0eS4U<;c{3s&O&I3BqRyFd~C;s1}j4 zuO&DrC;{px5npIw2#H3|FD<8Ear+x8huf3H6mi>0n>BT)a)_Pc(W6J~^$l6}iSUop z1MJ^B;u{bU3wL|ESQ?tY0BY!;9sGu<>>){((x$9h^OFtLlQdBHX*)@b!G^A)@r^@m zM1gxTS#$l9VP`a?6G-TH*S!P{R7(V$7l!#BOVo3mQbExW7rgxx)Tt|1ud2NBU1X#t z-inj2b+`g<)#md%`HL4XN=ixDtno+NLYee%k75gf0FTq^8wtGd!Ct#0~{SZKQBM!-^g7&eyEWA$mvK+JD5z|Kvo& zIFVZyGvoh}RF-=ptJTc^Cv7{w@1N*Oih{a&3~bw1u3h6m;05l2JDZ)8GcF0f#SX|; z%^F~%MnBx-R&O*SkKk992vXr+pyhdZn(P*);7=hY?}7A=^=qBUzJDZS%;|B#z<^`d zuHytHfB*eGR`|SM6&0yQ&u_EHevH28r^1u32Jl73E%?%+W;ZQ5j` zHdb9#1=9L#&zEnBkbZV@huj(L4386a^0eVG&P4d+*njXF@VnNT%=e?8_yv~7!l(8g zzAkLKT386q#2mq9f`UmXLm%I-7O~p?J&V_jdGc3Us%-ceQVcQQVYjK(D3Z6gD1`nC zirgsh%322Etpz$vPFfVMvY-EBf`8rGnir8p_L-&+36U|shsa)=SBo;J8IK2JVnFND_e%rJXc#&}4=eg%W%is(sQ>kvAQnSv`#QWu8c0!YfN#>C#-6pz2 z3=_-VzMAu}z9^=7*pr92x=^_($JT`>c2714FNs!;^#wa;lDX~BwY)qr_h0h8W|nk) zaLdm~XR+iOxW-Y?DLi%P=2x-d8_OK00o)B}(QD`*_a9i3K>53GnzflV{c6gKaui-d z7VDpMW(V&>s~fzWqz}Wxg94Ln@4F{dhPaUqZ!u$RG>HAt5D&v(z(qk2_2Ma*NYv-P zke+mPAJd(rSPZ|KOLCpD^I{fJhYu!3Coh+TA(J{z9lGUy?Zie1YET*eev zn}WX+yE+!01z^fLICO+>ZKJ|0rbH-E#IySRUzQP}RA>DX?T=+M?a+6fK3w0s3HB>k z)?!d{W}=NN#eKm|hi;knhyFa4w`2Ls=!*4|jbLWb9^j8Dqlj9LlnDj^>5jlb5P*vY zAe8%~OF$>-Eu6>aQ0u+{omz&PplqyO$HHl>S6R0KT};hQ=t4e8I}Bs_$1rR7bqSS#sEn4KGRpBS^WT)mk~(-7jQ2_p9!sKI97YeYBx@WVL>drG=f zz#aF2lmhZ*JGu98aFhy<)hMC|T<8-lfmVJ~)xq-@U=nkLcc~26zKpbXqNnN&#F==; z&0KfW2Cuz%aqOUFoA}+JAoJY0agDnV9vCHL4G2{OW^!D+Qu^^GM!*RvLkhAM>aA2q zbT+vN|ES5nbX7JNzVS3K$ZM-Xja-)umZwgU%+jazgr% z7vSfw9qn(dkJHZar&!Ssjm=pmK+&ln9Ujhsr&6KC*y>|t43wh@ln1r207xv@rEWAUsb4uBdTQVJXAd@2mroBFJ@IEobUeK#cOu#tGxL(wTM_sA9y3HX-C~eAu@4 zUheGh8Sh{!TCn&m+wvvdZ{drWJ9itQ`DI|3*o6`NMM@5BUytRhJ=7vzHu4Jh4dxmd z0)}RX@YbPqI?Z&51EgDz*?smRWXI`%RrX4M3$+NSw zqBWa<){zo6GtxsLlap{!GZ38?>Bz?t)kh!wLjVVQLpi|+o5{Grjm|Nn0-il zIp^WM9vr1mJZy`Y?2EUAq9wcD#$Ik289wlGXTS;EEz^(f8E9KNYn-_(-AQ=-9d#NF zk*?2qp-QP@71&M=>3YwB$S0-|{F@^VeRXF@KP>6bU+8rOd21gCbLFB{79AO^bA!@{ zXvnHzY>E;44j-Q4J$1EIzIvhn&82FL2hMq*BV@EjQC6$&Y}+XAI}4Wp@9A!;gUmvGTh_LEuU`Aw8tg4Q(-27U1FWASi;iWbe6hAokWQD&TnXk ztUq}&JAdXu6Nw^B)GbWkacz?om(#;DDmpk!E>DBwKL*_g03tP5cs&VOC`#aZWfKs1 z_Trf{?2d4#5GVJ5EB3GgEWJ+SpV=fx^^UH&1dt6z2#2KSb@$vL_d}HHGnOBtPD{7a z(MA!5V$o*>VO9>p>fnl-r3xrh#=0vDI=arHZ5G(ud3JkwZL)!^_b?5oP_Sf_rU79? z;T+x9VU+=@v{16~rO$JQiC;(E+o`adz);}hFJRUiup~d$<)WA;h!ardmhQ6eVEjUh zaXdMM035o{lG7B4e^5}nl_v90fzp(LZMT_y5k&%PtVhh>W!xnD< zh1gxzS%`~!I1c7ayg@*JgLdt=CJu=|w&b!PqUs?%=p&Jq!4NMz&jmMK^c|j%UY9Qu z1yc?n>8?|j1{ar?3!;Nl04D+PqAlZ-VBVA$8yA-yODn33I~PFG^tISSARcM^mCipu zU%VQ6-$67jS|h$lmLH{LIN{Om3z+%eQ8LG=1YTEm9akoaKoBJ5Vt+i^b`FxGJZ8?4 zUN?x~r>02XNCcYU$nW>?@>X$bW!7{!z^~~z-4iJd2a;#AxggA!2e4+Ip5HgL)S`vX zU|}H2@W{mAM$L@uIo$uM9jbc*cor6Zb|N5Uh^Nkjy#A+KLK61DvjcX*T-v@s0n4%$ z0)DKfC`2{<97}XWEtrz83X^`@azsSp*FbPrsMh-OAeed}sRW@JeA&lZfiCz@0UQ#a zt){ey!>G)CABM&~QuiMrOB+GE@O?N^6Rqg`aAj`&*M8wg5m`$pzB@c9;{2q)2r+KD z-@-k1=kcqCIMt=IWESd(3fMa&=SVm+`@95OZhu7^S#OA`37RJH+TbLGiq`pYL2vix zZm`foJsN{T5{nQ(%;K!@fXbnfH4!F{JqU(#I0B;v$yMa77{~%*bthT~T3utw=@;-c zySkv6iAYQ!4PisB0kaL$dGlk5{4SH0-a_Og0JL&9 z1v3DpLu^N9i4KxpEdY@1?BvCXFT`07xsW^)%-QfRsvEhBPPQ#W>gmRe(ZXpN6-Qb z^jJhzY z3A+}lu5d07XOH`m6ExyUfs&RkMoKTVUU-uRb0q8@mW9b{$j+*|t>xrGh2AXS%vfE= zYE`Ev@)qI|UsTj{5mZUP8quKvYIDZln>YbovF~@!CF&fSYe`p7?$oDkEE;XJT1B7^v4wqwG;HL}G!MqOlu_~{zo$G>2jRe~9?+rQt_Zzb;_Zle9`sf*#kJ}$s*R$;Q;zb8VEIe{^Q7p1c@3T$6yTi?B4x~ zoQ1N9fgymL&~TDA1=#M%QwWK}AIrdd8l;gRb~6#E`3konK5QF#v6g!a)l&^Pnj|Ki z_+;s~&>iY6oKOm}*^6}qAqbO*?2fAuNX=9D26A-A{1Ok2<+9?i^rL_eCryRq3;|Zu zQiNFdN5nk!0I{tuw8gewOEn>IWFl}ujRxoyibEPQ#PEDtpx_j73$;=5yC$B&0Zz{G zQ8u4}%0gVPpvv~Z&yMjYF%={R!}bM)J95T^)596GMOWiI9rPVsx9X&05skxMHvyx? zU^=0R_}kl@l}6+dG^F)JM+RjY=Dnpy6hK?pKA08IibNXqZAQE0C8~f|#;`a!cz7zQ z-ve~p>*U=)v7id<`2-pTJfd!6u{vxJSr%13ILpv0s*VjgV8Un7BJ4d5*h-GW>c>}V zHWGw%`t)z4L9Ru$GG~VbqPOirhH|V5ZQkB%Nt@&+)oLb+M{EPMMJ2F7*e_WVTdAOq zNrO2!bcZn)6j!5@E}!4=V5Ul_)8%S@!z;qjiUN}@IiZ4>Q&4mOggI7T86bm*HWzY_ zK1b~Ayh<`~+J;W2sKkPE);m_$!F__bVr?{VL3}+&|ZoVda`S?a@2}M)nh@<&rgj`nOO66E-$`C)849y5QQ%Na9 z?kXn0Jo9zOJG3&So_oGjWGH`GZ&OAxF@{{6{mghHSqP`zn72*^a}eJdX?BPAl6h?b zhw%)$+oDtYF=+ev`IXaJoMKto;6ftRg-8kVbWzUGbkoS-`_#TmCWqdVj+IoftVEwj zD(6<8#}NRE_5qpCij**rA3-S7P%+gBvMe(N07PEq=)HYi4HO94zntWhFE5cG%)f5^^R0&Yr43t9FUc zI`ns=m;libMY_YGpw_~&B3v>{fDM9Lq+qLyBDsAWrV$R99s@;*76y=Gj7-0HJ{Zn^ zz&e%3c|7t^@mSd)2$Rm{g7ykzs5r3jq&1aH1p3|8+qa+-anbsQDC>lCo>(PFHw9|^ z$Jq7G!b=>%$Jijw^*z4ETt#pckT*F7g=~+kfea!Ng8bMA`GuU*fkuzG$RH<48kdvU z!&pET()Oy7sJ+-tXISB(I=3{Pw`PcO6D8f4u-8paeev|njp-Ld?_9wCF-L;JcN|1) zBV_|law{jNV4b$&J|<9jA4#wlghAc`WFSFc2SMDSAZj8TmS6!h^($+Oz~?_WpnHiB zBZTn4@fpZqo;-+>1H34+<5sU(!%M0GSc4B@Nce!ca%Lv6o8EwJpaO>)fZUaWf>-py zkm@-FXkdo>H$+h_g}u-;gG1boS3?(31lZ~r9JCs zzGvhs#+ib}8^N880Fd*Xp~73hjN~fGp-I|Q;EC@ zt6OzoLR=VLSOOZ+b{~avzert>-p2hhmK>$n?W3w)Ne-Mt^9P)a;^f2*($dubtbH3f z_Xhe15tHy$BESoXP=f8t@@1Fe^e4_F@QkD-npkQe%_9m$fOvDhHw9s#$$B0;G!eZq z@<7%WspV!k7ZSbA(4o2!$CUtmQ_+nLD{<0$vjRa_RL0;1B)(>_5sWy?rzjwg(q{a7 r`;Tus!v^B2`A?T@|L#XWEi60l)TBzeTTYzO6p0fuKPMbJcj