PyHierarchicalTsetlinMachineCUDA 0.2.2__tar.gz → 0.2.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (16) hide show
  1. {PyHierarchicalTsetlinMachineCUDA-0.2.2 → PyHierarchicalTsetlinMachineCUDA-0.2.5}/PKG-INFO +1 -1
  2. {PyHierarchicalTsetlinMachineCUDA-0.2.2 → PyHierarchicalTsetlinMachineCUDA-0.2.5}/PyHierarchicalTsetlinMachineCUDA/kernels.py +48 -6
  3. {PyHierarchicalTsetlinMachineCUDA-0.2.2 → PyHierarchicalTsetlinMachineCUDA-0.2.5}/PyHierarchicalTsetlinMachineCUDA/tm.py +197 -21
  4. PyHierarchicalTsetlinMachineCUDA-0.2.5/PyHierarchicalTsetlinMachineCUDA/utils.py +175 -0
  5. {PyHierarchicalTsetlinMachineCUDA-0.2.2 → PyHierarchicalTsetlinMachineCUDA-0.2.5}/PyHierarchicalTsetlinMachineCUDA.egg-info/PKG-INFO +1 -1
  6. {PyHierarchicalTsetlinMachineCUDA-0.2.2 → PyHierarchicalTsetlinMachineCUDA-0.2.5}/PyHierarchicalTsetlinMachineCUDA.egg-info/SOURCES.txt +2 -5
  7. {PyHierarchicalTsetlinMachineCUDA-0.2.2 → PyHierarchicalTsetlinMachineCUDA-0.2.5}/PyHierarchicalTsetlinMachineCUDA.egg-info/requires.txt +3 -0
  8. PyHierarchicalTsetlinMachineCUDA-0.2.5/README.md +192 -0
  9. {PyHierarchicalTsetlinMachineCUDA-0.2.2 → PyHierarchicalTsetlinMachineCUDA-0.2.5}/setup.py +3 -2
  10. PyHierarchicalTsetlinMachineCUDA-0.2.2/README.md +0 -107
  11. {PyHierarchicalTsetlinMachineCUDA-0.2.2 → PyHierarchicalTsetlinMachineCUDA-0.2.5}/LICENSE +0 -0
  12. {PyHierarchicalTsetlinMachineCUDA-0.2.2 → PyHierarchicalTsetlinMachineCUDA-0.2.5}/PyHierarchicalTsetlinMachineCUDA/__init__.py +0 -0
  13. {PyHierarchicalTsetlinMachineCUDA-0.2.2 → PyHierarchicalTsetlinMachineCUDA-0.2.5}/PyHierarchicalTsetlinMachineCUDA/tools.py +0 -0
  14. {PyHierarchicalTsetlinMachineCUDA-0.2.2 → PyHierarchicalTsetlinMachineCUDA-0.2.5}/PyHierarchicalTsetlinMachineCUDA.egg-info/dependency_links.txt +0 -0
  15. {PyHierarchicalTsetlinMachineCUDA-0.2.2 → PyHierarchicalTsetlinMachineCUDA-0.2.5}/PyHierarchicalTsetlinMachineCUDA.egg-info/top_level.txt +0 -0
  16. {PyHierarchicalTsetlinMachineCUDA-0.2.2 → PyHierarchicalTsetlinMachineCUDA-0.2.5}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PyHierarchicalTsetlinMachineCUDA
3
- Version: 0.2.2
3
+ Version: 0.2.5
4
4
  Summary: Hierarchical Tsetlin Machine Architecture.
5
5
  Home-page: https://github.com/cair/PyHierarchicalTsetlinMachineCUDA/
6
6
  Author: Ole-Christoffer Granmo
@@ -215,10 +215,13 @@ code_update = """
215
215
  int or_group_vote_sum = 0;
216
216
  for (int or_addend = 0; or_addend < number_of_or_group_addends; ++or_addend) {
217
217
  // Aggregate votes from each child node through addition
218
+
219
+ int previous_or_group_vote_sum = or_group_vote_sum;
218
220
  or_group_vote_sum += child_input[or_group_node*number_of_or_group_addends + or_addend];
219
- //if (child_input[or_group_node*number_of_or_group_addends + or_addend]) {
220
- // or_group_vote_sum = 1;
221
- //}
221
+ if (or_group_vote_sum < 0) {
222
+ printf("OR* OVERFLOW %d -> %d\\n", previous_or_group_vote_sum, or_group_vote_sum);
223
+ or_group_vote_sum = previous_or_group_vote_sum;
224
+ }
222
225
  }
223
226
 
224
227
  // Store or group vote sum as node output
@@ -237,7 +240,14 @@ code_update = """
237
240
  int and_group_vote_product = 1;
238
241
  for (int and_factor = 0; and_factor < number_of_and_group_factors; ++and_factor) {
239
242
  // Aggregate votes from each child node through multiplication
243
+
244
+ int previous_and_group_vote_product = and_group_vote_product;
240
245
  and_group_vote_product *= child_input[and_group_node*number_of_and_group_factors + and_factor];
246
+
247
+ if (and_group_vote_product < 0) {
248
+ printf("AND OVERFLOW %d -> %d\\n", previous_and_group_vote_product, and_group_vote_product);
249
+ and_group_vote_product = previous_and_group_vote_product;
250
+ }
241
251
  }
242
252
 
243
253
  // Store and group product as node output
@@ -273,11 +283,15 @@ code_update = """
273
283
  int or_alternatives_vote_sum = 0;
274
284
  for (int or_alternative = 0; or_alternative < number_of_or_alternatives; ++or_alternative) {
275
285
  // Aggregate same input or alternatives through summation
286
+
287
+
288
+ int previous_or_alternatives_vote_sum = or_alternatives_vote_sum;
276
289
  or_alternatives_vote_sum += child_input[or_alternatives_node * number_of_or_alternatives + or_alternative];
277
290
 
278
- //if (child_input[or_alternatives_node * number_of_or_alternatives + or_alternative]) {
279
- // or_alternatives_vote_sum = 1;
280
- //}
291
+ if (or_alternatives_vote_sum < 0) {
292
+ printf("OR OVERFLOW %d -> %d\\n", previous_or_alternatives_vote_sum, or_alternatives_vote_sum);
293
+ or_alternatives_vote_sum = previous_or_alternatives_vote_sum;
294
+ }
281
295
  }
282
296
 
283
297
  // Store vote sum as node output
@@ -483,3 +497,31 @@ code_transform = """
483
497
  }
484
498
  }
485
499
  """
500
+
501
+ code_clauses = """
502
+ extern "C" __global__ void get_ta_states(const unsigned int* global_ta_state, unsigned int* unpacked_states) {
503
+ int index = blockIdx.x * blockDim.x + threadIdx.x;
504
+ int stride = blockDim.x * gridDim.x;
505
+
506
+ for (unsigned long long i = index; i < CLAUSES * COMPONENTS * LITERALS_PER_LEAF; i += stride) {
507
+ unsigned long long clause = i / (COMPONENTS * LITERALS_PER_LEAF);
508
+ unsigned long long comp = (i / LITERALS_PER_LEAF) % COMPONENTS;
509
+ unsigned long long ta_idx = i % LITERALS_PER_LEAF;
510
+
511
+ int chunk = ta_idx / 32;
512
+ int bit_pos = ta_idx % 32;
513
+
514
+ unsigned int state = 0;
515
+ for (int b = 0; b < STATE_BITS; ++b) {
516
+ unsigned int plane = global_ta_state[
517
+ (clause * COMPONENTS * TA_CHUNKS_PER_LEAF * STATE_BITS) +
518
+ (comp * TA_CHUNKS_PER_LEAF * STATE_BITS) +
519
+ (chunk * STATE_BITS) + b
520
+ ];
521
+ if (plane & (1U << bit_pos)) state |= (1U << b);
522
+ }
523
+
524
+ unpacked_states[(clause * COMPONENTS * LITERALS_PER_LEAF) + (comp * LITERALS_PER_LEAF) + ta_idx] = state;
525
+ }
526
+ }
527
+ """
@@ -18,6 +18,7 @@
18
18
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
19
  # SOFTWARE.
20
20
 
21
+ from collections import deque
21
22
  import numpy as np
22
23
 
23
24
  import PyHierarchicalTsetlinMachineCUDA.kernels as kernels
@@ -26,6 +27,7 @@ import pycuda.curandom as curandom
26
27
  import pycuda.driver as cuda
27
28
  import pycuda.autoinit
28
29
  from pycuda.compiler import SourceModule
30
+ from pycuda import gpuarray
29
31
 
30
32
  from time import time
31
33
 
@@ -37,11 +39,10 @@ VANILLA_TM = 0
37
39
  WEIGHTED_TM = 1
38
40
  COALESCED_TM = 2
39
41
 
40
- g = curandom.XORWOWRandomNumberGenerator()
41
42
 
42
43
  class CommonTsetlinMachine():
43
44
 
44
- def __init__(self, number_of_clauses, T, s, q=1.0, hierarchy_structure=None, boost_true_positive_feedback=1, number_of_state_bits=8, append_negated=True, grid=(16*13,1,1), block=(128,1,1)):
45
+ def __init__(self, number_of_clauses, T, s, q=1.0, hierarchy_structure=None, boost_true_positive_feedback=1, number_of_state_bits=8, append_negated=True, grid=(16*13,1,1), block=(128,1,1), seed=None):
45
46
  self.number_of_clauses = number_of_clauses
46
47
  self.number_of_state_bits = number_of_state_bits
47
48
  self.T = int(T)
@@ -95,6 +96,12 @@ class CommonTsetlinMachine():
95
96
  if (self.hierarchy_structure[d][0] == OR_GROUP or self.hierarchy_structure[d][0] == AND_GROUP):
96
97
  self.number_of_literal_chunks *= self.hierarchy_structure[d][1]
97
98
 
99
+ self.np_rng = np.random.default_rng(seed)
100
+ if seed is not None:
101
+ self.cuda_rng = curandom.XORWOWRandomNumberGenerator(seed_getter=lambda N: gpuarray.to_gpu(self.np_rng.integers(1, 2**30, size=N).astype(np.int32)))
102
+ else:
103
+ self.cuda_rng = curandom.XORWOWRandomNumberGenerator()
104
+
98
105
  self.cuda_modules()
99
106
 
100
107
  self.first = True
@@ -151,6 +158,9 @@ class CommonTsetlinMachine():
151
158
  self.prepare_encode_hierarchy = mod_encode.get_function("prepare_encode_hierarchy")
152
159
  self.encode_hierarchy = mod_encode.get_function("encode_hierarchy")
153
160
 
161
+ mod_clauses = SourceModule(parameters + kernels.code_clauses, no_extern_c=True)
162
+ self.kernel_get_ta_states = mod_clauses.get_function("get_ta_states")
163
+
154
164
  def encode_X(self, X, encoded_X_hierarchy_gpu):
155
165
  number_of_examples = X.shape[0]
156
166
 
@@ -171,8 +181,8 @@ class CommonTsetlinMachine():
171
181
  # GPU memory for accumulating votes, level by level
172
182
  self.hierarchy_votes = []
173
183
  for d in range(1, self.depth):
174
- self.hierarchy_votes.append(cuda.mem_alloc(self.number_of_clauses*int(self.hierarchy_size[d])*4))
175
- self.hierarchy_votes.append(cuda.mem_alloc(self.number_of_clauses*4))
184
+ self.hierarchy_votes.append(cuda.mem_alloc(self.number_of_clauses*int(self.hierarchy_size[d])*8))
185
+ self.hierarchy_votes.append(cuda.mem_alloc(self.number_of_clauses*8))
176
186
 
177
187
  # GPU memory for storing hierarchy structure
178
188
  self.hierarchy_structure_factors_gpu = cuda.mem_alloc((self.depth-1)*4)
@@ -236,10 +246,10 @@ class CommonTsetlinMachine():
236
246
  None # To be updated
237
247
 
238
248
  def initialize_weights_and_ta_states(self):
239
- self.prepare_weights(g.state, np.int32(self.tm_type), np.int32(self.number_of_outputs), self.clause_weights_gpu, self.class_sum_gpu, grid=self.grid, block=self.block)
249
+ self.prepare_weights(self.cuda_rng.state, np.int32(self.tm_type), np.int32(self.number_of_outputs), self.clause_weights_gpu, self.class_sum_gpu, grid=self.grid, block=self.block)
240
250
  cuda.Context.synchronize()
241
251
 
242
- self.prepare_hierarchy(g.state, np.int32(self.number_of_outputs), self.ta_state_hierarchy_gpu, self.clause_weights_gpu, self.class_sum_gpu, grid=self.grid, block=self.block)
252
+ self.prepare_hierarchy(self.cuda_rng.state, np.int32(self.number_of_outputs), self.ta_state_hierarchy_gpu, self.clause_weights_gpu, self.class_sum_gpu, grid=self.grid, block=self.block)
243
253
  cuda.Context.synchronize()
244
254
 
245
255
  def evaluate_hierarchy(self, encoded_X_hierarchy, e):
@@ -351,7 +361,7 @@ class CommonTsetlinMachine():
351
361
  self.update_hierarchy.prepared_call(
352
362
  self.grid,
353
363
  self.block,
354
- g.state,
364
+ self.cuda_rng.state,
355
365
  np.int32(self.number_of_outputs),
356
366
  self.ta_state_hierarchy_gpu,
357
367
  self.clause_weights_gpu,
@@ -371,7 +381,7 @@ class CommonTsetlinMachine():
371
381
  self.update_weights.prepared_call(
372
382
  self.grid,
373
383
  self.block,
374
- g.state,
384
+ self.cuda_rng.state,
375
385
  np.int32(self.tm_type),
376
386
  np.int32(self.number_of_outputs),
377
387
  self.clause_weights_gpu,
@@ -402,9 +412,102 @@ class CommonTsetlinMachine():
402
412
 
403
413
  return class_sum
404
414
 
415
+ def get_ta_states(self) -> np.ndarray:
416
+ """
417
+ Get state value for each TA.
418
+ Returns: Numpy array of shape (number_of_clauses, number_of_clause_components, number_of_literals_per_leaf)
419
+ """
420
+ # Mem Allocation
421
+ ta_states_gpu = gpuarray.to_gpu(
422
+ np.zeros((self.number_of_clauses, self.hierarchy_size[1], self.number_of_literals_per_leaf), dtype=np.uint32)
423
+ )
424
+
425
+ # Calculate grid size based on the kernel
426
+ total = self.number_of_clauses * self.hierarchy_size[1] * self.number_of_literals_per_leaf
427
+ grid = (((total + self.block[0] - 1) // self.block[0]), 1, 1)
428
+ self.kernel_get_ta_states(self.ta_state_hierarchy_gpu, ta_states_gpu, block=self.block, grid=grid)
429
+
430
+ # Copy back to CPU
431
+ return ta_states_gpu.get()
432
+
433
+ def get_literals(self):
434
+ """
435
+ Get included literals for each clause.
436
+ Returns: Numpy array of shape (number_of_clauses, number_of_clause_components, number_of_literals_per_leaf)
437
+ """
438
+ return (self.get_ta_states() >= (1 << (self.number_of_state_bits - 1))).astype(np.uint8)
439
+
440
+ def map_ta_id_to_feature_id(self):
441
+ """
442
+ Return an array of shape(number_of_clause_components, number_of_literals_per_leaf). That is the total number of TAs in a clause. Maps each TA id to a feature_id in the input. In each component, the first half of the TAs correspond to the positive features, and the second half correspond to the negated features.
443
+ """
444
+ # BFS top-down traversal
445
+ q = deque()
446
+ q.append((self.depth - 1, 0, 0)) # (level, node_id, group_id)
447
+
448
+ comp_grps = -1 * np.ones(self.hierarchy_size[1], dtype=np.int32)
449
+ while q:
450
+ level, node_id, group_id = q.popleft()
451
+
452
+ if level == 0:
453
+ # This is the leaf component
454
+ comp_grps[node_id] = group_id
455
+ continue
456
+
457
+ n_children = self.hierarchy_structure[level][1]
458
+ is_alt = (self.hierarchy_structure[level][0] == OR_ALTERNATIVES)
459
+ for child_pos in range(n_children):
460
+ child_id = node_id * n_children + child_pos
461
+ if is_alt:
462
+ # All children share the same features
463
+ child_group_id = group_id
464
+ else:
465
+ # Features are partitioned among the children
466
+ child_group_id = group_id * n_children + child_pos
467
+
468
+ q.append((level - 1, child_id, child_group_id))
469
+
470
+ # map each TA in a component to a feature
471
+ half = self.number_of_literals_per_leaf // 2
472
+ lit_ids = np.arange(self.number_of_literals_per_leaf)
473
+ local_feats = lit_ids % half if self.append_negated else lit_ids
474
+ fmap = comp_grps[:, None] * (half if self.append_negated else self.number_of_literals_per_leaf) + local_feats[None, :]
475
+
476
+ return fmap
477
+
478
+ def calc_hierarchy_votes(self, X):
479
+ """
480
+ Get the clause activation information for each sample in X.
481
+ """
482
+ assert not self.first, "Model must be trained before getting activations."
483
+
484
+ number_of_examples = X.shape[0]
485
+ encoded_X_hierarchy_test_gpu = gpuarray.empty((number_of_examples, self.number_of_literal_chunks), dtype=np.uint32)
486
+ self.encode_X(X, encoded_X_hierarchy_test_gpu.gpudata)
487
+
488
+ class_sum = np.ascontiguousarray(np.zeros((self.number_of_outputs, number_of_examples))).astype(np.int32)
489
+ class_sum_example = np.ascontiguousarray(np.zeros(self.number_of_outputs)).astype(np.int32)
490
+ hierarchy_votes = []
491
+ for e in range(number_of_examples):
492
+ self.evaluate_hierarchy(encoded_X_hierarchy_test_gpu.gpudata, e)
493
+
494
+ cuda.memcpy_dtoh(class_sum_example, self.class_sum_gpu)
495
+ class_sum[:, e] = class_sum_example
496
+
497
+ hierarchy_votes_example = []
498
+ for d in range(self.depth):
499
+ temp_arr = np.empty(self.number_of_clauses*int(self.hierarchy_size[d+1]), dtype=np.int32)
500
+ cuda.memcpy_dtoh(temp_arr, self.hierarchy_votes[d])
501
+ hierarchy_votes_example.append(temp_arr.reshape((self.number_of_clauses, int(self.hierarchy_size[d+1]))))
502
+
503
+ hierarchy_votes.append(hierarchy_votes_example)
504
+
505
+ class_sum = np.clip(class_sum.reshape((self.number_of_outputs, number_of_examples)), -self.T, self.T)
506
+ return hierarchy_votes, class_sum
507
+
405
508
  def print_hierarchy(self, print_ta_state=False):
406
509
  for i in range(self.number_of_clauses):
407
- print("\n CLAUSE #%d: " % (i), end='')
510
+ print("\nCLAUSE #%d: " % (i), end='')
408
511
 
409
512
  previous_index = np.ones((self.depth-1), dtype=np.int32)*-1
410
513
  for j in range(self.hierarchy_size[1]):
@@ -457,11 +560,53 @@ class CommonTsetlinMachine():
457
560
  print(l[0], end = '')
458
561
 
459
562
  print(")" * (self.depth - 1))
563
+
564
+ def save(self) -> dict:
565
+ ta_state_hierarchy = np.empty(self.number_of_clauses*self.hierarchy_size[0]*self.number_of_state_bits, dtype=np.uint32)
566
+ cuda.memcpy_dtoh(ta_state_hierarchy, self.ta_state_hierarchy_gpu)
567
+
568
+ clause_weights = np.empty(self.number_of_outputs*self.number_of_clauses, dtype=np.int32)
569
+ cuda.memcpy_dtoh(clause_weights, self.clause_weights_gpu)
570
+
571
+ return {
572
+ 'ta_state_hierarchy': ta_state_hierarchy,
573
+ 'clause_weights': clause_weights,
574
+ 'number_of_outputs': self.number_of_outputs,
575
+ 'min_y': self.min_y,
576
+ 'max_y': self.max_y,
577
+ 'params': {
578
+ 'number_of_clauses': self.number_of_clauses,
579
+ 'T': self.T,
580
+ 's': self.s,
581
+ 'q': self.q,
582
+ 'hierarchy_structure': self.hierarchy_structure,
583
+ 'boost_true_positive_feedback': self.boost_true_positive_feedback,
584
+ 'number_of_state_bits': self.number_of_state_bits,
585
+ 'append_negated': self.append_negated,
586
+ },
587
+ 'negative_clauses': self.negative_clauses,
588
+ 'tm_type': self.tm_type,
589
+ 'flip_polarity': self.flip_polarity,
590
+ 'weighted_clauses': getattr(self, 'weighted_clauses', None),
591
+ }
592
+
593
+ def load(self, state_dict: dict):
594
+ self.number_of_outputs = state_dict['number_of_outputs']
595
+ self.min_y = state_dict['min_y']
596
+ self.max_y = state_dict['max_y']
597
+
598
+ self.allocate_gpu_memory()
599
+
600
+ cuda.memcpy_htod(self.ta_state_hierarchy_gpu, state_dict['ta_state_hierarchy'])
601
+ cuda.memcpy_htod(self.clause_weights_gpu, state_dict['clause_weights'])
602
+
603
+ self.first = False
604
+
460
605
 
461
606
  class MultiOutputTsetlinMachine(CommonTsetlinMachine):
462
- def __init__(self, number_of_clauses, T, s, q=1.0, boost_true_positive_feedback=1, number_of_state_bits=8, append_negated=True, grid=(16*13,1,1), block=(128,1,1)):
607
+ def __init__(self, number_of_clauses, T, s, q=1.0, boost_true_positive_feedback=1, number_of_state_bits=8, append_negated=True, grid=(16*13,1,1), block=(128,1,1), seed=None):
463
608
  self.negative_clauses = 1
464
- super().__init__(number_of_clauses, T, s, q=q, boost_true_positive_feedback=boost_true_positive_feedback, number_of_state_bits=number_of_state_bits, append_negated=append_negated, grid=grid, block=block)
609
+ super().__init__(number_of_clauses, T, s, q=q, boost_true_positive_feedback=boost_true_positive_feedback, number_of_state_bits=number_of_state_bits, append_negated=append_negated, grid=grid, block=block, seed=seed)
465
610
 
466
611
  def fit(self, X, Y, epochs=100, incremental=False):
467
612
  X = X.reshape(X.shape[0], X.shape[1], 1)
@@ -485,12 +630,12 @@ class MultiOutputTsetlinMachine(CommonTsetlinMachine):
485
630
  return (self.score(X) >= 0).astype(np.uint32).transpose()
486
631
 
487
632
  class MultiClassCoalescedTsetlinMachine(CommonTsetlinMachine):
488
- def __init__(self, number_of_clauses, T, s, hierarchy_structure=((AND_GROUP, 1)), q=1.0, boost_true_positive_feedback=1, number_of_state_bits=8, append_negated=True, grid=(16*13,1,1), block=(128,1,1)):
633
+ def __init__(self, number_of_clauses, T, s, hierarchy_structure=((AND_GROUP, 1)), q=1.0, boost_true_positive_feedback=1, number_of_state_bits=8, append_negated=True, grid=(16*13,1,1), block=(128,1,1), seed=None):
489
634
  self.negative_clauses = 1
490
635
  self.tm_type = COALESCED_TM
491
636
  self.flip_polarity = 1
492
637
 
493
- super().__init__(number_of_clauses, T, s, hierarchy_structure=hierarchy_structure, q=q, boost_true_positive_feedback=boost_true_positive_feedback, number_of_state_bits=number_of_state_bits, append_negated=append_negated, grid=grid, block=block)
638
+ super().__init__(number_of_clauses, T, s, hierarchy_structure=hierarchy_structure, q=q, boost_true_positive_feedback=boost_true_positive_feedback, number_of_state_bits=number_of_state_bits, append_negated=append_negated, grid=grid, block=block, seed=seed)
494
639
 
495
640
  def fit(self, X, Y, epochs=100, incremental=False):
496
641
  X = X.reshape(X.shape[0], X.shape[1], 1)
@@ -517,7 +662,7 @@ class MultiClassCoalescedTsetlinMachine(CommonTsetlinMachine):
517
662
  return np.argmax(self.score(X), axis=0)
518
663
 
519
664
  class MultiClassTsetlinMachine:
520
- def __init__(self, number_of_clauses, T, s, weighted_clauses=False, hierarchy_structure=((AND_GROUP, 1)), q=1.0, boost_true_positive_feedback=1, number_of_state_bits=8, append_negated=True, grid=(16*13,1,1), block=(128,1,1)):
665
+ def __init__(self, number_of_clauses, T, s, weighted_clauses=False, hierarchy_structure=((AND_GROUP, 1)), q=1.0, boost_true_positive_feedback=1, number_of_state_bits=8, append_negated=True, grid=(16*13,1,1), block=(128,1,1), seed=None):
521
666
  self.number_of_clauses = number_of_clauses
522
667
  self.T = T
523
668
  self.s = s
@@ -529,19 +674,20 @@ class MultiClassTsetlinMachine:
529
674
  self.append_negated = append_negated
530
675
  self.grid = grid
531
676
  self.block = block
677
+ self.seed = np.random.randint(1, 2**30) if seed is None else seed
532
678
 
533
679
  self.configured = False
534
680
 
535
681
  def fit(self, X, Y, epochs=100, incremental=False):
536
682
  self.number_of_outputs = int(np.max(Y) + 1)
537
-
683
+
538
684
  if not self.configured:
539
685
  self.tms = []
540
686
  for i in range(self.number_of_outputs):
541
- self.tms.append(TsetlinMachine(self.number_of_clauses, self.T, self.s, weighted_clauses=self.weighted_clauses, hierarchy_structure=self.hierarchy_structure, q=self.q, boost_true_positive_feedback=self.boost_true_positive_feedback, number_of_state_bits=self.number_of_state_bits, append_negated=self.append_negated, grid=self.grid, block=self.block))
687
+ self.tms.append(TsetlinMachine(self.number_of_clauses, self.T, self.s, weighted_clauses=self.weighted_clauses, hierarchy_structure=self.hierarchy_structure, q=self.q, boost_true_positive_feedback=self.boost_true_positive_feedback, number_of_state_bits=self.number_of_state_bits, append_negated=self.append_negated, grid=self.grid, block=self.block, seed=self.seed+i))
542
688
 
543
689
  self.configured = True
544
-
690
+
545
691
  encoded_Y = np.empty(Y.shape[0], dtype = np.int32)
546
692
 
547
693
  for epoch in range(epochs):
@@ -569,8 +715,38 @@ class MultiClassTsetlinMachine:
569
715
  def predict(self, X):
570
716
  return np.argmax(self.score(X), axis=0)
571
717
 
718
+ def save(self) -> dict:
719
+ return {
720
+ 'number_of_outputs': self.number_of_outputs,
721
+ 'tms': [t.save() for t in self.tms],
722
+ 'params': {
723
+ 'number_of_clauses': self.number_of_clauses,
724
+ 'T': self.T,
725
+ 's': self.s,
726
+ 'q': self.q,
727
+ 'weighted_clauses': self.weighted_clauses,
728
+ 'hierarchy_structure': self.hierarchy_structure,
729
+ 'boost_true_positive_feedback': self.boost_true_positive_feedback,
730
+ 'number_of_state_bits': self.number_of_state_bits,
731
+ 'append_negated': self.append_negated,
732
+ 'grid': self.grid,
733
+ 'block': self.block,
734
+ },
735
+ }
736
+
737
+ def load(self, state_dict: dict):
738
+ self.number_of_outputs = state_dict['number_of_outputs']
739
+
740
+ self.tms = []
741
+ for tm_state in state_dict['tms']:
742
+ t = TsetlinMachine(self.number_of_clauses, self.T, self.s, weighted_clauses=self.weighted_clauses, hierarchy_structure=self.hierarchy_structure, q=self.q, boost_true_positive_feedback=self.boost_true_positive_feedback, number_of_state_bits=self.number_of_state_bits, append_negated=self.append_negated, grid=self.grid, block=self.block)
743
+ t.load(tm_state)
744
+ self.tms.append(t)
745
+
746
+ self.configured = True
747
+
572
748
  class TsetlinMachine(CommonTsetlinMachine):
573
- def __init__(self, number_of_clauses, T, s, weighted_clauses=False, hierarchy_structure=((AND_GROUP, 1)), q=1.0, boost_true_positive_feedback=1, number_of_state_bits=8, append_negated=True, grid=(16*13,1,1), block=(128,1,1)):
749
+ def __init__(self, number_of_clauses, T, s, weighted_clauses=False, hierarchy_structure=((AND_GROUP, 1)), q=1.0, boost_true_positive_feedback=1, number_of_state_bits=8, append_negated=True, grid=(16*13,1,1), block=(128,1,1), seed=None):
574
750
  self.negative_clauses = 1
575
751
  self.flip_polarity = 0
576
752
 
@@ -579,7 +755,7 @@ class TsetlinMachine(CommonTsetlinMachine):
579
755
  else:
580
756
  self.tm_type = VANILLA_TM
581
757
 
582
- super().__init__(number_of_clauses, T, s, hierarchy_structure=hierarchy_structure, q=q, boost_true_positive_feedback=boost_true_positive_feedback, number_of_state_bits=number_of_state_bits, append_negated=append_negated, grid=grid, block=block)
758
+ super().__init__(number_of_clauses, T, s, hierarchy_structure=hierarchy_structure, q=q, boost_true_positive_feedback=boost_true_positive_feedback, number_of_state_bits=number_of_state_bits, append_negated=append_negated, grid=grid, block=block, seed=seed)
583
759
 
584
760
  def fit(self, X, Y, epochs=100, incremental=False):
585
761
  X = X.reshape(X.shape[0], X.shape[1], 1)
@@ -603,11 +779,11 @@ class TsetlinMachine(CommonTsetlinMachine):
603
779
  return (self.score(X) >= 0).astype(np.int32)
604
780
 
605
781
  class RegressionTsetlinMachine(CommonTsetlinMachine):
606
- def __init__(self, number_of_clauses, T, s, hierarchy_structure=((AND_GROUP, 1)), boost_true_positive_feedback=1, number_of_state_bits=8, append_negated=True, grid=(16*13,1,1), block=(128,1,1)):
782
+ def __init__(self, number_of_clauses, T, s, hierarchy_structure=((AND_GROUP, 1)), boost_true_positive_feedback=1, number_of_state_bits=8, append_negated=True, grid=(16*13,1,1), block=(128,1,1), seed=None):
607
783
  self.negative_clauses = 0
608
784
  self.flip_polarity = 0
609
785
 
610
- super().__init__(number_of_clauses, T, s, hierarchy_structure=hierarchy_structure, boost_true_positive_feedback=boost_true_positive_feedback, number_of_state_bits=number_of_state_bits, append_negated=append_negated, grid=grid, block=block)
786
+ super().__init__(number_of_clauses, T, s, hierarchy_structure=hierarchy_structure, boost_true_positive_feedback=boost_true_positive_feedback, number_of_state_bits=number_of_state_bits, append_negated=append_negated, grid=grid, block=block, seed=seed)
611
787
 
612
788
  def fit(self, X, Y, epochs=100, incremental=False):
613
789
  X = X.reshape(X.shape[0], X.shape[1], 1)
@@ -0,0 +1,175 @@
1
+ from collections import deque
2
+
3
+ import networkx as nx
4
+ import numpy as np
5
+
6
+ from .tm import OR_ALTERNATIVES, CommonTsetlinMachine
7
+
8
+
9
+ def make_hierarchy_graph(G: nx.Graph, hier: list[tuple[int, int]]):
10
+ """Create Graph from the hierarchy."""
11
+ # BFS
12
+ q = deque()
13
+ q.append((len(hier), 0)) # (level, idx)
14
+ while q:
15
+ level, idx = q.popleft()
16
+ G.add_node((level, idx), label=hier[level - 1][0], op=hier[level - 1][0])
17
+ if level == 1:
18
+ continue
19
+ branching = hier[level - 1][1]
20
+ for cid in range(idx * branching, (idx + 1) * branching):
21
+ q.append((level - 1, cid))
22
+ G.add_edge((level, idx), (level - 1, cid))
23
+
24
+
25
+ def clause_to_nx(
26
+ tm: CommonTsetlinMachine,
27
+ clause_idx: int,
28
+ feature_names: list[str] | None = None,
29
+ negation_prefix: str = '¬',
30
+ clause_literals: np.ndarray | None = None,
31
+ ta_to_fid_mapping=None,
32
+ ):
33
+ """
34
+ Create a networkx Graph for a single clause.
35
+ Args:
36
+ `tm`: The model.
37
+ `clause_idx`: Index of the clause to visualize.
38
+ `feature_names`: Optional list of feature names for labeling. If None, defaults to 'x0', 'x1', ...
39
+ `negation_prefix`: Prefix for negated features (default: '¬').
40
+ `clause_literals`: Optional pre-extracted literals for the clause, must have shape (n_components, literals_per_component). If None, literals will be extracted from the model.
41
+ `ta_to_fid_mapping`: Optional mapping from (component_id, literal_id) to feature_id. If None, it will be obtained from the model.
42
+ """
43
+
44
+ assert tm.hierarchy_structure is not None, (
45
+ 'Hierarchy structure not defined in the model. Are you sure tm belongs to CommonTsetlinMachine or its subclasses?'
46
+ )
47
+
48
+ if feature_names is None:
49
+ feature_names = [f'x{i}' for i in range(tm.number_of_features_hierarchy)]
50
+
51
+ literals = (
52
+ clause_literals
53
+ if clause_literals is not None
54
+ else tm.get_literals()[clause_idx]
55
+ )
56
+ ta_to_fid = (
57
+ ta_to_fid_mapping
58
+ if ta_to_fid_mapping is not None
59
+ else tm.map_ta_id_to_feature_id()
60
+ )
61
+ n_comp = tm.hierarchy_size[1]
62
+ lits_per_comp = tm.number_of_literals_per_leaf
63
+
64
+ G = nx.Graph()
65
+ make_hierarchy_graph(G, tm.hierarchy_structure)
66
+
67
+ feat_per_comp = lits_per_comp // 2 if tm.append_negated else lits_per_comp
68
+
69
+ # Add included literals to leaf components
70
+ for comp_id in range(n_comp):
71
+ for fid in range(feat_per_comp):
72
+ pos_lit = literals[comp_id, fid]
73
+ if pos_lit:
74
+ G.add_node(
75
+ (0, comp_id * lits_per_comp + fid),
76
+ label=feature_names[ta_to_fid[comp_id, fid]],
77
+ )
78
+ G.add_edge((1, comp_id), (0, comp_id * lits_per_comp + fid))
79
+
80
+ if tm.append_negated:
81
+ neg_lit = literals[comp_id, feat_per_comp + fid]
82
+ if neg_lit:
83
+ lab = f'{negation_prefix}{feature_names[ta_to_fid[comp_id, fid]]}'
84
+ G.add_node(
85
+ (0, comp_id * lits_per_comp + feat_per_comp + fid), label=lab
86
+ )
87
+ G.add_edge(
88
+ (1, comp_id), (0, comp_id * lits_per_comp + feat_per_comp + fid)
89
+ )
90
+
91
+ return G
92
+
93
+
94
+ def clause_bank_to_nx(
95
+ tm: CommonTsetlinMachine,
96
+ feature_names: list[str] | None = None,
97
+ negation_prefix: str = '¬',
98
+ ):
99
+ """
100
+ Create a networkx Graph for the entire clause bank.
101
+ Args:
102
+ `tm`: The model.
103
+ `feature_names`: Optional list of feature names for labeling. If None, defaults to 'x0', 'x1', ...
104
+ `negation_prefix`: Prefix for negated features (default: '¬').
105
+ """
106
+ literals = tm.get_literals()
107
+ ta_to_fid = tm.map_ta_id_to_feature_id()
108
+ G = nx.Graph()
109
+ cb_root = 'CB_ROOT'
110
+ G.add_node(cb_root, label=OR_ALTERNATIVES, op=OR_ALTERNATIVES)
111
+ for ci in range(tm.number_of_clauses):
112
+ clause_G = clause_to_nx(
113
+ tm, ci, feature_names, negation_prefix, literals[ci], ta_to_fid
114
+ )
115
+ mapping = {node: (ci, node) for node in clause_G.nodes}
116
+ G = nx.compose(G, nx.relabel_nodes(clause_G, mapping))
117
+ G.add_edge(cb_root, (ci, (tm.depth, 0)))
118
+
119
+ return G
120
+
121
+
122
+ def active_path_graph(
123
+ tm: CommonTsetlinMachine,
124
+ X: np.ndarray,
125
+ feature_names: list[str] | None = None,
126
+ negation_prefix: str = '¬',
127
+ ):
128
+ """
129
+ Show the activated path, and vote calculation for a single sample `X`.
130
+ Args:
131
+ `tm`: The model.
132
+ `X`: A single sample with shape (n_features,).
133
+ `feature_names`: Optional list of feature names for labeling. If None, defaults to 'x0', 'x1', ...
134
+ `negation_prefix`: Prefix for negated features (default: '¬').
135
+ Returns:
136
+ A list of networkx Graphs for each clause, with 'active' and 'vote' attributes on nodes and edges, and class sums for each class.
137
+ """
138
+ assert X.ndim == 1, 'X must be a single sample with shape (n_features,)'
139
+
140
+ hierarchy_votes, class_sums = tm.calc_hierarchy_votes(X.reshape(1, -1))
141
+ hierarchy_votes = hierarchy_votes[0]
142
+ class_sums = class_sums[0]
143
+
144
+ literals = tm.get_literals()
145
+ ta_to_fid = tm.map_ta_id_to_feature_id()
146
+ lits_per_comp = tm.number_of_literals_per_leaf
147
+ feat_per_comp = lits_per_comp // 2 if tm.append_negated else lits_per_comp
148
+
149
+ G = [
150
+ clause_to_nx(tm, ci, feature_names, negation_prefix, literals[ci], ta_to_fid)
151
+ for ci in range(tm.number_of_clauses)
152
+ ]
153
+ for ci in range(tm.number_of_clauses):
154
+ g = G[ci]
155
+
156
+ for node in g.nodes():
157
+ level, idx = node
158
+ if level == 0:
159
+ comp_id = idx // lits_per_comp
160
+ local_lit = idx % lits_per_comp
161
+ fid = ta_to_fid[comp_id, local_lit]
162
+ is_neg = (local_lit >= feat_per_comp) if tm.append_negated else False
163
+ g.nodes[node]['active'] = (X[fid] == 0) if is_neg else (X[fid] == 1)
164
+ g.nodes[node]['vote'] = 1 if g.nodes[node]['active'] else None
165
+ else:
166
+ vote = hierarchy_votes[level - 1][ci, idx]
167
+ g.nodes[node]['active'] = vote > 0
168
+ g.nodes[node]['vote'] = vote
169
+
170
+ for u, v in g.edges():
171
+ g.edges[u, v]['active'] = g.nodes[u].get('active', False) and g.nodes[
172
+ v
173
+ ].get('active', False)
174
+
175
+ return G, class_sums
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PyHierarchicalTsetlinMachineCUDA
3
- Version: 0.2.2
3
+ Version: 0.2.5
4
4
  Summary: Hierarchical Tsetlin Machine Architecture.
5
5
  Home-page: https://github.com/cair/PyHierarchicalTsetlinMachineCUDA/
6
6
  Author: Ole-Christoffer Granmo
@@ -5,12 +5,9 @@ PyHierarchicalTsetlinMachineCUDA/__init__.py
5
5
  PyHierarchicalTsetlinMachineCUDA/kernels.py
6
6
  PyHierarchicalTsetlinMachineCUDA/tm.py
7
7
  PyHierarchicalTsetlinMachineCUDA/tools.py
8
+ PyHierarchicalTsetlinMachineCUDA/utils.py
8
9
  PyHierarchicalTsetlinMachineCUDA.egg-info/PKG-INFO
9
10
  PyHierarchicalTsetlinMachineCUDA.egg-info/SOURCES.txt
10
11
  PyHierarchicalTsetlinMachineCUDA.egg-info/dependency_links.txt
11
12
  PyHierarchicalTsetlinMachineCUDA.egg-info/requires.txt
12
- PyHierarchicalTsetlinMachineCUDA.egg-info/top_level.txt
13
- pyhierarchicaltsetlinmachinecuda.egg-info/PKG-INFO
14
- pyhierarchicaltsetlinmachinecuda.egg-info/SOURCES.txt
15
- pyhierarchicaltsetlinmachinecuda.egg-info/dependency_links.txt
16
- pyhierarchicaltsetlinmachinecuda.egg-info/top_level.txt
13
+ PyHierarchicalTsetlinMachineCUDA.egg-info/top_level.txt
@@ -2,6 +2,9 @@ numpy
2
2
  pycuda
3
3
  scipy
4
4
  scikit-learn
5
+ networkx
5
6
 
6
7
  [examples]
7
8
  tensorflow
9
+ matplotlib
10
+ pygraphviz
@@ -0,0 +1,192 @@
1
+ # Hierarchical Tsetlin Machine in CUDA
2
+ Implements the [Hierarchical Tsetlin Machine](https://github.com/cair/HierarchicalTsetlinMachine) in CUDA.
3
+
4
+ <p align="center">
5
+ <img width="60%" src="https://github.com/cair/PyHierarchicalTsetlinMachineCUDA/blob/main/figures/Visualization_voting.png">
6
+ </p>
7
+
8
+ ## Installation
9
+
10
+ ```bash
11
+ pip install PyHierarchicalTsetlinMachineCUDA
12
+ ```
13
+ or
14
+ ```bash
15
+ git clone git@github.com:cair/PyHierarchicalTsetlinMachineCUDA.git
16
+ cd PyHierarchicalTsetlinMachineCUDA
17
+ python ./setup.py sdist
18
+ pip install dist/PyHierarchicalTsetlinMachineCUDA-0.2.2.tar.gz
19
+ ```
20
+
21
+ ## Examples
22
+
23
+ ### Noisy Parity Demo
24
+
25
+ #### Code: NoisyParityDemo.py
26
+
27
+ ```python
28
+ from PyHierarchicalTsetlinMachineCUDA.tm import TsetlinMachine
29
+ import numpy as np
30
+ from time import time
31
+ import PyHierarchicalTsetlinMachineCUDA.tm as tm
32
+
33
+ clauses = 32
34
+ s = 25.0
35
+ T = 250
36
+
37
+ train_data = np.loadtxt("./examples/NoisyParityTrainingData.txt").astype(np.uint32)
38
+ X_train = train_data[:,0:-1]
39
+ Y_train = train_data[:,-1]
40
+
41
+ test_data = np.loadtxt("./examples/NoisyParityTestingData.txt").astype(np.uint32)
42
+ X_test = test_data[:,0:-1]
43
+ Y_test = test_data[:,-1]
44
+
45
+ tm = TsetlinMachine(clauses, T, s, number_of_state_bits=8, boost_true_positive_feedback=0, hierarchy_structure=((tm.AND_GROUP, 3), (tm.OR_ALTERNATIVES, 3), (tm.AND_GROUP, 2), (tm.OR_ALTERNATIVES, 3), (tm.AND_GROUP, 2)))
46
+
47
+ print("\nAccuracy over 1000 epochs:\n")
48
+ for e in range(1000):
49
+ start_training = time()
50
+ tm.fit(X_train, Y_train, epochs=10, incremental=True)
51
+ stop_training = time()
52
+
53
+ start_testing = time()
54
+ result = 100*(tm.predict(X_test) == Y_test).mean()
55
+ stop_testing = time()
56
+
57
+ tm.print_hierarchy()
58
+
59
+ print("\n#%d Accuracy: %.2f%% Training: %.2fs Testing: %.2fs" % (e+1, result, stop_training-start_training, stop_testing-start_testing))
60
+ ```
61
+
62
+ #### Output
63
+
64
+ ```bash
65
+ python ./examples/NoisyParityData.py
66
+ python ./examples/NoisyParityDemo.py
67
+
68
+ Accuracy over 1000 epochs:
69
+
70
+ #1 Accuracy: 51.19% Training: 21.88s Testing: 1.37s
71
+ #2 Accuracy: 51.94% Training: 21.91s Testing: 1.37s
72
+ #3 Accuracy: 53.46% Training: 21.91s Testing: 1.37s
73
+ ...
74
+ CLAUSE 1: (((((x0 ∧ x1) ∨ (x0 ∧ x1) ∨ (¬x0 ∧ ¬x1)) ∧ ((¬x0 ∧ ¬x1) ∨ (x0 ∧ x1) ∨ (x0 ∧ x1))) ∨ (((¬x0 ∧ ¬x1) ∨ (¬x0 ∧ ¬x1) ∨ (x0 ∧ x1)) ∧ ((x0 ∧ x1) ∨ (x0 ∧ x1) ∨ (¬x0 ∧ ¬x1))) ∨ (((x0 ∧ x1) ∨ (x0 ∧ x1) ∨ (¬x0 ∧ ¬x1)) ∧ (¬x0 ∨ (x0 ∧ x1) ∨ (¬x0 ∧ ¬x1)))) ∧ ((((x0 ∧ ¬x1) ∨ (x0 ∧ ¬x1) ∨ (x1 ∧ ¬x0)) ∧ ((x0 ∧ x1) ∨ (¬x0 ∧ ¬x1) ∨ (¬x0 ∧ ¬x1))) ∨ (((x1 ∧ ¬x0) ∨ (x0 ∧ ¬x1) ∨ (x0 ∧ ¬x1)) ∧ ((x0 ∧ x1) ∨ (¬x0 ∧ ¬x1) ∨ (x0 ∧ x1))) ∨ (((x1 ∧ ¬x0) ∨ (x1 ∧ ¬x0) ∨ (x0 ∧ ¬x1)) ∧ ((x0 ∧ x1) ∨ (¬x0 ∧ ¬x1) ∨ (¬x0 ∧ ¬x1)))))
75
+ ...
76
+ #778 Accuracy: 99.71% Training: 32.57s Testing: 2.18s
77
+ ```
78
+
79
+ ### Hex Demo
80
+
81
+ <p align="center">
82
+ <img width="60%" src="https://github.com/cair/PyHierarchicalTsetlinMachineCUDA/blob/hex_description/figures/Hex-board-11x11.png">
83
+ </p>
84
+ <p align="center">
85
+ Source: https://en.wikipedia.org/wiki/Hex_(board_game)
86
+ </p>
87
+
88
+ #### Code: HexDemo.py
89
+
90
+ ```python
91
+ from PyHierarchicalTsetlinMachineCUDA.tm import TsetlinMachine
92
+ import numpy as np
93
+ from time import time
94
+ import PyHierarchicalTsetlinMachineCUDA.tm as tm
95
+ import argparse
96
+
97
+ def default_args(**kwargs):
98
+ parser = argparse.ArgumentParser()
99
+ parser.add_argument("--epochs", default=1000, type=int)
100
+ parser.add_argument("--clauses", default=2000, type=int)
101
+ parser.add_argument("--T", default=9000, type=int)
102
+ parser.add_argument("--s", default=44.0, type=float)
103
+ parser.add_argument("--q", default=1.0, type=float)
104
+ parser.add_argument("--boost", default=1, type=int)
105
+ parser.add_argument("--number_of_state_bits", default=7, type=int)
106
+ parser.add_argument("--or_alternatives", default=60, type=int)
107
+
108
+ args = parser.parse_args()
109
+ for key, value in kwargs.items():
110
+ if key in args.__dict__:
111
+ setattr(args, key, value)
112
+ return args
113
+
114
+ args = default_args()
115
+
116
+ data = np.loadtxt("./examples/hex_data.txt").astype(np.uint32)
117
+ X_train = data[:int(len(data)*0.8),0:-1]
118
+ Y_train = data[:int(len(data)*0.8),-1]
119
+
120
+ X_test = data[int(len(data)*0.8):,0:-1]
121
+ Y_test = data[int(len(data)*0.8):,-1]
122
+
123
+ tsetlin_machine = TsetlinMachine(args.clauses, args.T, args.s, weighted_clauses=False, number_of_state_bits=args.number_of_state_bits, boost_true_positive_feedback=args.boost, hierarchy_structure=((tm.AND_GROUP, 72), (tm.OR_ALTERNATIVES, args.or_alternatives), (tm.AND_GROUP, 4)))
124
+
125
+ print("\nAccuracy over %d epochs:\n" % (args.epochs))
126
+ for e in range(args.epochs):
127
+ start_training = time()
128
+ for b in range(10):
129
+ tsetlin_machine.fit(X_train[b*len(Y_train)//10:(b+1)*len(Y_train)//10], Y_train[b*len(Y_train)//10:(b+1)*len(Y_train)//10], epochs=1, incremental=True)
130
+ stop_training = time()
131
+
132
+ start_testing = time()
133
+ result_testing = 100*(tsetlin_machine.predict(X_test) == Y_test).mean()
134
+ stop_testing = time()
135
+
136
+ result_training = 100*(tsetlin_machine.predict(X_train) == Y_train).mean()
137
+
138
+ print("#%d Training Accuracy: %.2f%% Testing Accuracy: %.2f%% Training Time: %.2fs Testing Time: %.2fs" % (e+1, result_training, result_testing, stop_training-start_training, stop_testing-start_testing))
139
+ ```
140
+
141
+ #### Output
142
+
143
+ ```bash
144
+ cd ./examples
145
+ make
146
+ ./hex
147
+ cd ..
148
+ python3 ./examples/HexDemo.py
149
+
150
+ Accuracy over 1000 epochs:
151
+
152
+ #1 Training Accuracy: 97.71% Testing Accuracy: 97.26% Training Time: 36.00s Testing Time: 5.65s
153
+ #2 Training Accuracy: 99.11% Testing Accuracy: 98.61% Training Time: 33.65s Testing Time: 5.65s
154
+ #3 Training Accuracy: 99.53% Testing Accuracy: 98.92% Training Time: 33.00s Testing Time: 5.65s
155
+ #4 Training Accuracy: 99.74% Testing Accuracy: 99.18% Training Time: 32.66s Testing Time: 5.67s
156
+ #5 Training Accuracy: 99.84% Testing Accuracy: 99.35% Training Time: 32.40s Testing Time: 5.67s
157
+ ...
158
+ #446 Training Accuracy: 100.00% Testing Accuracy: 99.45% Training Time: 32.09s Testing Time: 6.09s
159
+ #447 Training Accuracy: 100.00% Testing Accuracy: 99.46% Training Time: 32.08s Testing Time: 6.09s
160
+ #448 Training Accuracy: 100.00% Testing Accuracy: 99.45% Training Time: 32.10s Testing Time: 6.09s
161
+ #449 Training Accuracy: 100.00% Testing Accuracy: 99.47% Training Time: 32.06s Testing Time: 6.07s
162
+ #450 Training Accuracy: 100.00% Testing Accuracy: 99.45% Training Time: 31.96s Testing Time: 6.08s
163
+ ...
164
+ ```
165
+
166
+ ## Paper
167
+
168
+ _A Tsetlin Machine for Logical Learning and Reasoning With AND-OR Hierarchies_. Ole-Christoffer Granmo, et al., 2026. (Forthcoming)
169
+
170
+ ## Licence
171
+
172
+ MIT License
173
+
174
+ Copyright (c) 2026 Ole-Christoffer Granmo and the University of Agder
175
+
176
+ Permission is hereby granted, free of charge, to any person obtaining a copy
177
+ of this software and associated documentation files (the "Software"), to deal
178
+ in the Software without restriction, including without limitation the rights
179
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
180
+ copies of the Software, and to permit persons to whom the Software is
181
+ furnished to do so, subject to the following conditions:
182
+
183
+ The above copyright notice and this permission notice shall be included in all
184
+ copies or substantial portions of the Software.
185
+
186
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
187
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
188
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
189
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
190
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
191
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
192
+ SOFTWARE.
@@ -2,7 +2,7 @@ from setuptools import *
2
2
 
3
3
  setup(
4
4
  name='PyHierarchicalTsetlinMachineCUDA',
5
- version='0.2.2',
5
+ version='0.2.5',
6
6
  author='Ole-Christoffer Granmo',
7
7
  author_email='ole.granmo@uia.no',
8
8
  url='https://github.com/cair/PyHierarchicalTsetlinMachineCUDA/',
@@ -16,8 +16,9 @@ setup(
16
16
  'pycuda',
17
17
  'scipy',
18
18
  'scikit-learn',
19
+ 'networkx',
19
20
  ],
20
21
  extras_require={
21
- 'examples': ['tensorflow'],
22
+ 'examples': ['tensorflow', 'matplotlib', 'pygraphviz'],
22
23
  }
23
24
  )
@@ -1,107 +0,0 @@
1
- # Hierarchical Tsetlin Machine in CUDA
2
- Implements the [Hierarchical Tsetlin Machine](https://github.com/cair/HierarchicalTsetlinMachine) in CUDA.
3
-
4
- <p align="center">
5
- <img width="70%" src="https://github.com/cair/PyHierarchicalTsetlinMachineCUDA/blob/main/figures/Clause_Plot_Mayur_Shende.png">
6
- </p>
7
-
8
- ## Installation
9
-
10
- ```bash
11
- pip install pyhierarchicaltsetlinmachinecuda
12
- ```
13
- or
14
- ```bash
15
- git clone git@github.com:cair/PyHierarchicalTsetlinMachineCUDA.git
16
- cd PyHierarchicalTsetlinMachineCUDA
17
- python ./setup.py sdist
18
- pip install dist/pyhierarchicaltsetlinmachinecuda-0.2.1.tar.gz
19
- ```
20
-
21
- ## Examples
22
-
23
- ### Noisy Parity Demo
24
-
25
- #### Code: NoisyParityDemo.py
26
-
27
- ```python
28
- from PyHierarchicalTsetlinMachineCUDA.tm import TsetlinMachine
29
- import numpy as np
30
- from time import time
31
- import PyHierarchicalTsetlinMachineCUDA.tm as tm
32
-
33
- train_data = np.loadtxt("./examples/NoisyParityTrainingData.txt").astype(np.uint32)
34
- X_train = train_data[:,0:-1]
35
- Y_train = train_data[:,-1]
36
-
37
- test_data = np.loadtxt("./examples/NoisyParityTestingData.txt").astype(np.uint32)
38
- X_test = test_data[:,0:-1]
39
- Y_test = test_data[:,-1]
40
-
41
- tm = TsetlinMachine(32, 3000, 30.1,
42
- number_of_state_bits=8,
43
- boost_true_positive_feedback=0,
44
- hierarchy_structure=(
45
- (tm.AND_GROUP, 3),
46
- (tm.OR_ALTERNATIVES, 10),
47
- (tm.AND_GROUP, 2),
48
- (tm.OR_ALTERNATIVES, 2),
49
- (tm.AND_GROUP, 2)
50
- )
51
- )
52
-
53
- print("\nAccuracy over 500 epochs:\n")
54
- for i in range(500):
55
- start_training = time()
56
- tm.fit(X_train, Y_train, epochs=10, incremental=True)
57
- stop_training = time()
58
-
59
- start_testing = time()
60
- result = 100*(tm.predict(X_test) == Y_test).mean()
61
- stop_testing = time()
62
-
63
- print("#%d Accuracy: %.2f%% Training: %.2fs Testing: %.2fs" % (i+1, result, stop_training-start_training, stop_testing-start_testing))
64
- ```
65
-
66
- #### Output
67
-
68
- ```bash
69
- python ./examples/NoisyParityData.py
70
- python ./examples/NoisyParityDemo.py
71
-
72
- #1 Accuracy: 58.54% Training: 22.36s Testing: 1.39s
73
- #2 Accuracy: 61.03% Training: 22.40s Testing: 1.39s
74
- #3 Accuracy: 72.43% Training: 22.42s Testing: 1.39s
75
- ...
76
- #88 Accuracy: 99.87% Training: 24.39s Testing: 1.51s
77
- #89 Accuracy: 99.38% Training: 24.40s Testing: 1.52s
78
- #90 Accuracy: 99.94% Training: 24.39s Testing: 1.52s
79
- ```
80
-
81
- ### Paper
82
-
83
- _A Tsetlin Machine for Logical Learning and Reasoning With AND-OR Hierarchies_. Ole-Christoffer Granmo, et al., 2026. (Forthcoming)
84
-
85
- ## Licence
86
-
87
- MIT License
88
-
89
- Copyright (c) 2026 Ole-Christoffer Granmo and the University of Agder
90
-
91
- Permission is hereby granted, free of charge, to any person obtaining a copy
92
- of this software and associated documentation files (the "Software"), to deal
93
- in the Software without restriction, including without limitation the rights
94
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
95
- copies of the Software, and to permit persons to whom the Software is
96
- furnished to do so, subject to the following conditions:
97
-
98
- The above copyright notice and this permission notice shall be included in all
99
- copies or substantial portions of the Software.
100
-
101
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
102
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
103
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
104
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
105
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
106
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
107
- SOFTWARE.