PyHierarchicalTsetlinMachineCUDA 0.2.8__tar.gz → 0.3.0__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 (15) hide show
  1. {PyHierarchicalTsetlinMachineCUDA-0.2.8 → PyHierarchicalTsetlinMachineCUDA-0.3.0}/PKG-INFO +1 -1
  2. {PyHierarchicalTsetlinMachineCUDA-0.2.8 → PyHierarchicalTsetlinMachineCUDA-0.3.0}/PyHierarchicalTsetlinMachineCUDA/kernels.py +47 -394
  3. {PyHierarchicalTsetlinMachineCUDA-0.2.8 → PyHierarchicalTsetlinMachineCUDA-0.3.0}/PyHierarchicalTsetlinMachineCUDA/tm.py +139 -137
  4. {PyHierarchicalTsetlinMachineCUDA-0.2.8 → PyHierarchicalTsetlinMachineCUDA-0.3.0}/PyHierarchicalTsetlinMachineCUDA.egg-info/PKG-INFO +1 -1
  5. {PyHierarchicalTsetlinMachineCUDA-0.2.8 → PyHierarchicalTsetlinMachineCUDA-0.3.0}/README.md +1 -1
  6. {PyHierarchicalTsetlinMachineCUDA-0.2.8 → PyHierarchicalTsetlinMachineCUDA-0.3.0}/setup.py +1 -1
  7. {PyHierarchicalTsetlinMachineCUDA-0.2.8 → PyHierarchicalTsetlinMachineCUDA-0.3.0}/LICENSE +0 -0
  8. {PyHierarchicalTsetlinMachineCUDA-0.2.8 → PyHierarchicalTsetlinMachineCUDA-0.3.0}/PyHierarchicalTsetlinMachineCUDA/__init__.py +0 -0
  9. {PyHierarchicalTsetlinMachineCUDA-0.2.8 → PyHierarchicalTsetlinMachineCUDA-0.3.0}/PyHierarchicalTsetlinMachineCUDA/tools.py +0 -0
  10. {PyHierarchicalTsetlinMachineCUDA-0.2.8 → PyHierarchicalTsetlinMachineCUDA-0.3.0}/PyHierarchicalTsetlinMachineCUDA/utils.py +0 -0
  11. {PyHierarchicalTsetlinMachineCUDA-0.2.8 → PyHierarchicalTsetlinMachineCUDA-0.3.0}/PyHierarchicalTsetlinMachineCUDA.egg-info/SOURCES.txt +0 -0
  12. {PyHierarchicalTsetlinMachineCUDA-0.2.8 → PyHierarchicalTsetlinMachineCUDA-0.3.0}/PyHierarchicalTsetlinMachineCUDA.egg-info/dependency_links.txt +0 -0
  13. {PyHierarchicalTsetlinMachineCUDA-0.2.8 → PyHierarchicalTsetlinMachineCUDA-0.3.0}/PyHierarchicalTsetlinMachineCUDA.egg-info/requires.txt +0 -0
  14. {PyHierarchicalTsetlinMachineCUDA-0.2.8 → PyHierarchicalTsetlinMachineCUDA-0.3.0}/PyHierarchicalTsetlinMachineCUDA.egg-info/top_level.txt +0 -0
  15. {PyHierarchicalTsetlinMachineCUDA-0.2.8 → PyHierarchicalTsetlinMachineCUDA-0.3.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PyHierarchicalTsetlinMachineCUDA
3
- Version: 0.2.8
3
+ Version: 0.3.0
4
4
  Summary: Hierarchical Tsetlin Machine Architecture.
5
5
  Home-page: https://github.com/cair/PyHierarchicalTsetlinMachineCUDA/
6
6
  Author: Ole-Christoffer Granmo
@@ -114,36 +114,6 @@ code_update = """
114
114
  }
115
115
  }
116
116
 
117
- __device__ inline void update_clause_weight_old(curandState *localState, int tm_type, int number_of_outputs, int *clause_weight, int clause_output, int y, int class_sum)
118
- {
119
- int target = 1 - 2*(class_sum > y);
120
-
121
- if (target == -1 && curand_uniform(localState) > 1.0*Q/max(1, number_of_outputs-1)) {
122
- return;
123
- }
124
-
125
- int sign = (*clause_weight >= 0) - (*clause_weight < 0);
126
-
127
- int absolute_prediction_error = abs(y - class_sum);
128
- if (curand_uniform(localState) <= 1.0*absolute_prediction_error/(2*THRESHOLD)) {
129
- if (target*sign > 0) {
130
- if (clause_output && abs(*clause_weight) < INT_MAX) {
131
- (*clause_weight) += sign;
132
- }
133
- } else if (target*sign < 0 && clause_output) {
134
- // Type II Feedback
135
-
136
- (*clause_weight) -= sign;
137
-
138
- #if FLIP_POLARITY == 0
139
- if (*clause_weight == 0) {
140
- *clause_weight += sign;
141
- }
142
- #endif
143
- }
144
- }
145
- }
146
-
147
117
  __device__ inline void update_component_hierarchy(curandState *localState, int number_of_outputs, int *clause_weight, unsigned int *ta_state, int component_output, int *X, int y, float class_sum)
148
118
  {
149
119
  int target = 1 - 2*(class_sum > y);
@@ -189,52 +159,6 @@ code_update = """
189
159
  }
190
160
  }
191
161
 
192
- __device__ inline void update_component_hierarchy_old(curandState *localState, int number_of_outputs, int *clause_weight, unsigned int *ta_state, int component_output, int *X, int y, int class_sum)
193
- {
194
- int target = 1 - 2*(class_sum > y);
195
-
196
- if (target == -1 && curand_uniform(localState) > 1.0*Q/max(1, number_of_outputs-1)) {
197
- return;
198
- }
199
-
200
-
201
- int sign = (*clause_weight >= 0) - (*clause_weight < 0);
202
-
203
- int absolute_prediction_error = abs(y - class_sum);
204
- if (curand_uniform(localState) <= 1.0*absolute_prediction_error/(2*THRESHOLD)) {
205
- if (target*sign > 0) {
206
- // Type I Feedback
207
- for (int ta_chunk = 0; ta_chunk < TA_CHUNKS_PER_LEAF; ++ta_chunk) {
208
- // Generate random bit values
209
- unsigned int la_feedback = 0;
210
- for (int b = 0; b < INT_SIZE; ++b) {
211
- if (curand_uniform(localState) <= 1.0/S) {
212
- la_feedback |= (1 << b);
213
- }
214
- }
215
-
216
- if (component_output) {
217
- #if BOOST_TRUE_POSITIVE_FEEDBACK == 1
218
- inc(ta_state, ta_chunk, X[ta_chunk]);
219
- #else
220
- inc(ta_state, ta_chunk, X[ta_chunk] & (~la_feedback));
221
- #endif
222
-
223
- dec(ta_state, ta_chunk, (~X[ta_chunk]) & la_feedback);
224
- } else {
225
- dec(ta_state, ta_chunk, la_feedback);
226
- }
227
- }
228
- } else if (target*sign < 0 && component_output) {
229
- // Type II Feedback
230
-
231
- for (int ta_chunk = 0; ta_chunk < TA_CHUNKS_PER_LEAF; ++ta_chunk) {
232
- inc(ta_state, ta_chunk, (~X[ta_chunk]) & (~ta_state[ta_chunk*STATE_BITS + STATE_BITS - 1]));
233
- }
234
- }
235
- }
236
- }
237
-
238
162
  // Evaluate example
239
163
  __global__ void evaluate_leaves(unsigned int *global_ta_state, int *component_weights, float *global_component_output, int depth, int *hierarchy_structure_factors, int *hierarchy_structure_type, int *X, int example)
240
164
  {
@@ -254,22 +178,24 @@ code_update = """
254
178
  int size_feature_chunk_base = 1;
255
179
  int size_ta_chunk_base = 1;
256
180
  for (int d = 0; d < depth-1; ++d) {
257
- int depth_d_node_index = component_remainder % hierarchy_structure_factors[d];
258
- component_remainder = component_remainder / hierarchy_structure_factors[d];
181
+ int depth_d_node_index = component_remainder % hierarchy_structure_factors[d]; // Extract local index of node at depth d (the child index)
182
+ component_remainder = component_remainder / hierarchy_structure_factors[d]; // Move up the hierarchy to prepare for finding the local node index at depth d+1
259
183
 
260
- if (hierarchy_structure_type[d] != 1) {
261
- feature_chunk_base += size_feature_chunk_base * depth_d_node_index * TA_CHUNKS_PER_LEAF;
262
- size_feature_chunk_base *= hierarchy_structure_factors[d];
184
+ // Support multiple Tsetlin Automata teams operating on the same feature chunks to create alternatives.
185
+ if ((hierarchy_structure_type[d] != OR_ALTERNATIVES) && (hierarchy_structure_type[d] != AND_ALTERNATIVES)) {
186
+ feature_chunk_base += size_feature_chunk_base * depth_d_node_index * TA_CHUNKS_PER_LEAF; // Identify correct part of the sub-hierarchy (through flattening)
187
+ size_feature_chunk_base *= hierarchy_structure_factors[d]; // Identify how many nodes to jump to skip from one child to the next when indexing at the next depth level
263
188
  }
264
189
 
265
- if (hierarchy_structure_type[d] != 2) {
190
+ // Support reuse of the same Tsetlin Automata when doing OR to group different feature chunks
191
+ if (hierarchy_structure_type[d] != OR_GROUP) {
266
192
  ta_chunk_base += size_ta_chunk_base * depth_d_node_index * TA_CHUNKS_PER_LEAF;
267
193
  size_ta_chunk_base *= hierarchy_structure_factors[d];
268
194
  }
269
195
  }
270
196
 
271
197
  // Get state of current ta team component
272
- unsigned int *ta_state = &global_ta_state[clause*COMPONENTS*TA_CHUNKS_PER_LEAF*STATE_BITS + ta_chunk_base*STATE_BITS];
198
+ unsigned int *ta_state = &global_ta_state[clause*TA_TEAMS*TA_CHUNKS_PER_LEAF*STATE_BITS + ta_chunk_base*STATE_BITS];
273
199
 
274
200
  // Evaluate clause component
275
201
 
@@ -304,60 +230,6 @@ code_update = """
304
230
  }
305
231
  }
306
232
 
307
- // Evaluate example
308
- __global__ void evaluate_leaves_old(unsigned int *global_ta_state, int *component_weights, float *global_component_output, int depth, int *hierarchy_structure_factors, int *hierarchy_structure_type, int *X, int example)
309
- {
310
- int index = blockIdx.x * blockDim.x + threadIdx.x;
311
- int stride = blockDim.x * gridDim.x;
312
-
313
- int *Xi = &X[(unsigned long long)example*LITERAL_CHUNKS];
314
-
315
- // Evaluate each clause component (leaf) in separate threads
316
- for (int clause_component = index; clause_component < CLAUSES*COMPONENTS; clause_component += stride) {
317
- int clause = clause_component / COMPONENTS;
318
- int component = clause_component % COMPONENTS;
319
-
320
- int component_remainder = component;
321
- int feature_chunk_base = 0;
322
- int ta_chunk_base = 0;
323
- int size_feature_chunk_base = 1;
324
- int size_ta_chunk_base = 1;
325
- for (int d = 0; d < depth-1; ++d) {
326
- int depth_d_node_index = component_remainder % hierarchy_structure_factors[d];
327
- component_remainder = component_remainder / hierarchy_structure_factors[d];
328
-
329
- if (hierarchy_structure_type[d] != 1) {
330
- feature_chunk_base += size_feature_chunk_base * depth_d_node_index * TA_CHUNKS_PER_LEAF;
331
- size_feature_chunk_base *= hierarchy_structure_factors[d];
332
- }
333
-
334
- if (hierarchy_structure_type[d] != 2) {
335
- ta_chunk_base += size_ta_chunk_base * depth_d_node_index * TA_CHUNKS_PER_LEAF;
336
- size_ta_chunk_base *= hierarchy_structure_factors[d];
337
- }
338
- }
339
-
340
- // Get state of current ta team component
341
- unsigned int *ta_state = &global_ta_state[clause*COMPONENTS*TA_CHUNKS_PER_LEAF*STATE_BITS + ta_chunk_base*STATE_BITS];
342
-
343
- // Evaluate clause component
344
- int component_output = 1;
345
- for (int ta_chunk = 0; ta_chunk < TA_CHUNKS_PER_LEAF-1; ++ta_chunk) {
346
- // Compare the TA state of the component (leaf) against the corresponding part of the feature vector
347
- if ((ta_state[ta_chunk*STATE_BITS + STATE_BITS - 1] & Xi[feature_chunk_base + ta_chunk]) != ta_state[ta_chunk*STATE_BITS + STATE_BITS - 1]) {
348
- component_output = 0;
349
- break;
350
- }
351
- }
352
-
353
- if ((ta_state[(TA_CHUNKS_PER_LEAF-1)*STATE_BITS + STATE_BITS - 1] & Xi[feature_chunk_base + TA_CHUNKS_PER_LEAF-1] & FILTER_HIERARCHICAL) != (ta_state[(TA_CHUNKS_PER_LEAF-1)*STATE_BITS + STATE_BITS - 1] & FILTER_HIERARCHICAL)) {
354
- component_output = 0;
355
- }
356
-
357
- global_component_output[clause_component] = component_output;
358
- }
359
- }
360
-
361
233
  __global__ void evaluate_or_groups(float *child_input, float *or_group_node_output, int number_of_or_group_nodes, int number_of_or_group_addends)
362
234
  {
363
235
  int index = blockIdx.x * blockDim.x + threadIdx.x;
@@ -410,38 +282,6 @@ code_update = """
410
282
  }
411
283
  }
412
284
 
413
- __global__ void evaluate_and_groups_old(float *child_input, float *and_group_node_output, int number_of_and_group_nodes, int number_of_and_group_factors)
414
- {
415
- int index = blockIdx.x * blockDim.x + threadIdx.x;
416
- int stride = blockDim.x * gridDim.x;
417
-
418
- // Multiply the votes from the children of each AND node
419
- for (int and_group_node = index; and_group_node < CLAUSES*number_of_and_group_nodes; and_group_node += stride) {
420
- // Multiply and factors
421
- int and_group_vote_product = 1;
422
- int max_vote_sum = 0;
423
- for (int and_factor = 0; and_factor < number_of_and_group_factors; ++and_factor) {
424
- // Aggregate votes from each child node through multiplication
425
-
426
- int previous_and_group_vote_product = and_group_vote_product;
427
- and_group_vote_product *= child_input[and_group_node*number_of_and_group_factors + and_factor];
428
-
429
- if (child_input[and_group_node*number_of_and_group_factors + and_factor] > max_vote_sum) {
430
- max_vote_sum = child_input[and_group_node*number_of_and_group_factors + and_factor];
431
- }
432
-
433
- if (and_group_vote_product < 0) {
434
- printf("AND OVERFLOW %d -> %d\\n", previous_and_group_vote_product, and_group_vote_product);
435
- and_group_vote_product = previous_and_group_vote_product;
436
- }
437
- }
438
-
439
- // Store and group product as node output
440
- and_group_node_output[and_group_node] = and_group_vote_product;
441
- //and_group_node_output[and_group_node] = max_vote_sum;
442
- }
443
- }
444
-
445
285
  __global__ void propagate_and_group_false_truth_values(float *child_input, float *group_node_output, int number_of_group_nodes, int number_of_group_node_children)
446
286
  //__global__ void propagate_and_group_false_truth_values_log(int *child_input, int *group_node_output, int number_of_group_nodes, int number_of_group_node_children)
447
287
  {
@@ -476,29 +316,12 @@ code_update = """
476
316
  }
477
317
  }
478
318
 
479
- __global__ void propagate_and_group_false_truth_values_old(float *child_input, float *group_node_output, int number_of_group_nodes, int number_of_group_node_children)
480
- {
481
- int index = blockIdx.x * blockDim.x + threadIdx.x;
482
- int stride = blockDim.x * gridDim.x;
483
-
484
- // If a group node is false, all children are made false.
485
- for (int group_node = index; group_node < CLAUSES*number_of_group_nodes; group_node += stride) {
486
- if (group_node_output[group_node] == 0) {
487
- for (int and_factor = 0; and_factor < number_of_group_node_children; ++and_factor) {
488
- if (child_input[group_node*number_of_group_node_children + and_factor] > 0) {
489
- child_input[group_node*number_of_group_node_children + and_factor] = 0;
490
- }
491
- }
492
- }
493
- }
494
- }
495
-
496
319
  __global__ void propagate_or_group_false_truth_values(curandState *state, float *child_input, float *group_node_output, int number_of_group_nodes, int number_of_group_node_children)
497
320
  {
498
321
  int index = blockIdx.x * blockDim.x + threadIdx.x;
499
322
  int stride = blockDim.x * gridDim.x;
500
323
 
501
- int non_zero_children[361];
324
+ int non_zero_children[MAX_NUMBER_OF_OR_GROUP_CHILDREN];
502
325
 
503
326
  /* Copy state to local memory for efficiency */
504
327
  curandState localState = state[index];
@@ -539,7 +362,7 @@ code_update = """
539
362
  }
540
363
  } else {
541
364
  for (int or_addend = 0; or_addend < number_of_group_node_children; ++or_addend) {
542
- if (child_input[group_node*number_of_group_node_children + or_addend] > 0) {
365
+ if (child_input[group_node*number_of_group_node_children + or_addend] > 0) { // Or pick the largest.... picking randomly when a tie
543
366
  non_zero_children[number_of_non_zero_children] = or_addend;
544
367
  number_of_non_zero_children++;
545
368
  }
@@ -566,58 +389,6 @@ code_update = """
566
389
  state[index] = localState;
567
390
  }
568
391
 
569
- __global__ void propagate_or_group_false_truth_values_old(curandState *state, float *child_input, float *group_node_output, int number_of_group_nodes, int number_of_group_node_children)
570
- {
571
- int index = blockIdx.x * blockDim.x + threadIdx.x;
572
- int stride = blockDim.x * gridDim.x;
573
-
574
- int non_zero_children[361];
575
-
576
- /* Copy state to local memory for efficiency */
577
- curandState localState = state[index];
578
-
579
- // If a group node is false, all children are made false.
580
- for (int group_node = index; group_node < CLAUSES*number_of_group_nodes; group_node += stride) {
581
- int number_of_non_zero_children = 0;
582
-
583
- if (group_node_output[group_node] == -1) {
584
- for (int or_addend = 0; or_addend < number_of_group_node_children; ++or_addend) {
585
- child_input[group_node*number_of_group_node_children + or_addend] = -1;
586
- }
587
- } else if (group_node_output[group_node] == 0) {
588
- for (int or_addend = 0; or_addend < number_of_group_node_children; ++or_addend) {
589
- if (child_input[group_node*number_of_group_node_children + or_addend] > 0) {
590
- child_input[group_node*number_of_group_node_children + or_addend] = 0;
591
- }
592
- }
593
- } else {
594
- for (int or_addend = 0; or_addend < number_of_group_node_children; ++or_addend) {
595
- if (child_input[group_node*number_of_group_node_children + or_addend] > 0) {
596
- non_zero_children[number_of_non_zero_children] = or_addend;
597
- number_of_non_zero_children++;
598
- }
599
- }
600
- }
601
-
602
- if (group_node_output[group_node] != -1) {
603
- int selected_child;
604
- if (number_of_non_zero_children > 0) {
605
- selected_child = non_zero_children[curand(&localState) % number_of_non_zero_children];
606
- } else {
607
- selected_child = curand(&localState) % number_of_group_node_children;
608
- }
609
-
610
- for (int or_addend = 0; or_addend < number_of_group_node_children; ++or_addend) {
611
- if (selected_child != or_addend) {
612
- child_input[group_node*number_of_group_node_children + or_addend] = -1;
613
- }
614
- }
615
- }
616
- }
617
-
618
- state[index] = localState;
619
- }
620
-
621
392
  __global__ void evaluate_or_alternatives(float *child_input, float *or_alternatives_node_output, int number_of_or_alternatives_nodes, int number_of_or_alternatives)
622
393
  //__global__ void evaluate_or_alternatives_log(int *child_input, int *or_alternatives_node_output, int number_of_or_alternatives_nodes, int number_of_or_alternatives)
623
394
  {
@@ -662,34 +433,6 @@ code_update = """
662
433
  }
663
434
  }
664
435
 
665
- __global__ void evaluate_or_alternatives_old(float *child_input, float *or_alternatives_node_output, int number_of_or_alternatives_nodes, int number_of_or_alternatives)
666
- {
667
- int index = blockIdx.x * blockDim.x + threadIdx.x;
668
- int stride = blockDim.x * gridDim.x;
669
-
670
- // Add up the votes from the children of each OR node
671
- for (int or_alternatives_node = index; or_alternatives_node < CLAUSES*number_of_or_alternatives_nodes; or_alternatives_node += stride) {
672
- // Sum up votes from each or alternative
673
- int or_alternatives_vote_sum = 0;
674
- for (int or_alternative = 0; or_alternative < number_of_or_alternatives; ++or_alternative) {
675
- // Aggregate same input or alternatives through summation
676
-
677
-
678
- int previous_or_alternatives_vote_sum = or_alternatives_vote_sum;
679
- or_alternatives_vote_sum += child_input[or_alternatives_node * number_of_or_alternatives + or_alternative];
680
-
681
- if (or_alternatives_vote_sum < 0) {
682
- printf("OR OVERFLOW %d -> %d\\n", previous_or_alternatives_vote_sum, or_alternatives_vote_sum);
683
- or_alternatives_vote_sum = previous_or_alternatives_vote_sum;
684
- }
685
- }
686
-
687
- // Store vote sum as node output
688
- or_alternatives_node_output[or_alternatives_node] = or_alternatives_vote_sum;
689
- }
690
- }
691
-
692
-
693
436
  __global__ void max_clause_output(int number_of_outputs, float *clause_output, float *clause_output_max)
694
437
  {
695
438
  int index = blockIdx.x * blockDim.x + threadIdx.x;
@@ -701,19 +444,36 @@ code_update = """
701
444
  }
702
445
  }
703
446
 
704
- __global__ void evaluate_final(int number_of_outputs, float *clause_output, float *clause_output_max, int *clause_weights, float *class_sum)
447
+ __global__ void evaluate_final(int number_of_outputs, float *clause_output, int *clause_weights, float *class_sum)
705
448
  {
706
449
  int index = blockIdx.x * blockDim.x + threadIdx.x;
707
450
  int stride = blockDim.x * gridDim.x;
708
451
 
709
452
  // Add up the votes from each clause
710
453
  #if LOG_SCALE == 1
711
- if (clause_output_max[0] != NEG_INFINITY) {
712
- for (int clause = index; clause < CLAUSES; clause += stride) {
713
- int output = exp2f(clause_output[clause] - clause_output_max[0]);
714
- for (int class_id = 0; class_id < number_of_outputs; ++class_id) {
715
- atomicAdd(&class_sum[class_id], (float) clause_weights[class_id*CLAUSES + clause] * output);
454
+ // Add up the votes from each clause
455
+ for (int class_id = index; class_id < number_of_outputs; class_id += stride) {
456
+ float clause_output_max = NEG_INFINITY;
457
+ for (int clause = 0; clause < CLAUSES; ++clause) {
458
+ if (clause_output[clause] > clause_output_max) {
459
+ clause_output_max = clause_output[clause];
460
+ }
461
+ }
462
+
463
+ if (clause_output_max != NEG_INFINITY) {
464
+ float weighted_clause_output_sum = 0;
465
+ for (int clause = 0; clause < CLAUSES; ++clause) {
466
+ weighted_clause_output_sum += clause_weights[class_id*CLAUSES + clause] * exp2f(clause_output[clause] - clause_output_max);
467
+ }
468
+
469
+ if (log2f(fabs(weighted_clause_output_sum)) + clause_output_max >= log2f(THRESHOLD)) {
470
+ float sign = (1 - 2 * (weighted_clause_output_sum < 0));
471
+ class_sum[class_id] = sign*THRESHOLD;
472
+ } else {
473
+ class_sum[class_id] = weighted_clause_output_sum * exp2f(clause_output_max);
716
474
  }
475
+ } else {
476
+ class_sum[class_id] = 0;
717
477
  }
718
478
  }
719
479
  #else
@@ -727,22 +487,6 @@ code_update = """
727
487
  #endif
728
488
  }
729
489
 
730
- __global__ void evaluate_final_old(int number_of_outputs, float *child_input, int *clause_weights, int *class_sum)
731
- {
732
- int index = blockIdx.x * blockDim.x + threadIdx.x;
733
- int stride = blockDim.x * gridDim.x;
734
-
735
- // Add up the votes from each clause
736
- for (int clause = index; clause < CLAUSES; clause += stride) {
737
- if (child_input[clause]) {
738
- for (int class_id = 0; class_id < number_of_outputs; ++class_id) {
739
- int clause_weight = clause_weights[class_id*CLAUSES + clause];
740
- atomicAdd(&class_sum[class_id], (int) clause_weight * child_input[clause]);
741
- }
742
- }
743
- }
744
- }
745
-
746
490
  __global__ void rescale_final(int number_of_outputs, float *clause_output_max, float *class_sum)
747
491
  {
748
492
  int index = blockIdx.x * blockDim.x + threadIdx.x;
@@ -797,19 +541,19 @@ code_update = """
797
541
  int depth_d_node_index = component_remainder % hierarchy_structure_factors[d];
798
542
  component_remainder = component_remainder / hierarchy_structure_factors[d];
799
543
 
800
- if (hierarchy_structure_type[d] != 1) {
544
+ if ((hierarchy_structure_type[d] != OR_ALTERNATIVES) && (hierarchy_structure_type[d] != AND_ALTERNATIVES)) {
801
545
  feature_chunk_base += size_feature_chunk_base * depth_d_node_index * TA_CHUNKS_PER_LEAF;
802
546
  size_feature_chunk_base *= hierarchy_structure_factors[d];
803
547
  }
804
548
 
805
- if (hierarchy_structure_type[d] != 2) {
549
+ if (hierarchy_structure_type[d] != OR_GROUP) {
806
550
  ta_chunk_base += size_ta_chunk_base * depth_d_node_index * TA_CHUNKS_PER_LEAF;
807
551
  size_ta_chunk_base *= hierarchy_structure_factors[d];
808
552
  }
809
553
  }
810
554
 
811
555
  // Get state of current ta team component
812
- unsigned int *ta_state = &global_ta_state[clause*COMPONENTS*TA_CHUNKS_PER_LEAF*STATE_BITS + ta_chunk_base*STATE_BITS];
556
+ unsigned int *ta_state = &global_ta_state[clause*TA_TEAMS*TA_CHUNKS_PER_LEAF*STATE_BITS + ta_chunk_base*STATE_BITS];
813
557
 
814
558
  for (unsigned long long class_id = 0; class_id < number_of_outputs; ++class_id) {
815
559
  float local_class_sum = class_sum[class_id];
@@ -830,68 +574,6 @@ code_update = """
830
574
  state[index] = localState;
831
575
  }
832
576
 
833
- // Update state of Tsetlin Automata team
834
- __global__ void update_hierarchy_old(curandState *state, int number_of_outputs, unsigned int *global_ta_state, int *clause_weights, float *component_output, int depth, int *hierarchy_structure_factors, int *hierarchy_structure_type, int *class_sum, int *X, int *y, int example)
835
- {
836
- int index = blockIdx.x * blockDim.x + threadIdx.x;
837
- int stride = blockDim.x * gridDim.x;
838
-
839
- /* Copy state to local memory for efficiency */
840
- curandState localState = state[index];
841
-
842
- int *Xi = &X[(unsigned long long)example*LITERAL_CHUNKS];
843
-
844
- // Calculate clause output first
845
- for (int clause_component = index; clause_component < CLAUSES*COMPONENTS; clause_component += stride) {
846
- if (component_output[clause_component] == -1) {
847
- continue;
848
- }
849
-
850
- int clause = clause_component / COMPONENTS;
851
- int component = clause_component % COMPONENTS;
852
-
853
- // Get state of current clause component
854
- // This one must be updated for reuse of TAs within OR group
855
-
856
- int component_remainder = component;
857
- int feature_chunk_base = 0;
858
- int ta_chunk_base = 0;
859
- int size_feature_chunk_base = 1;
860
- int size_ta_chunk_base = 1;
861
- for (int d = 0; d < depth-1; ++d) {
862
- int depth_d_node_index = component_remainder % hierarchy_structure_factors[d];
863
- component_remainder = component_remainder / hierarchy_structure_factors[d];
864
-
865
- if (hierarchy_structure_type[d] != 1) {
866
- feature_chunk_base += size_feature_chunk_base * depth_d_node_index * TA_CHUNKS_PER_LEAF;
867
- size_feature_chunk_base *= hierarchy_structure_factors[d];
868
- }
869
-
870
- if (hierarchy_structure_type[d] != 2) {
871
- ta_chunk_base += size_ta_chunk_base * depth_d_node_index * TA_CHUNKS_PER_LEAF;
872
- size_ta_chunk_base *= hierarchy_structure_factors[d];
873
- }
874
- }
875
-
876
- // Get state of current ta team component
877
- unsigned int *ta_state = &global_ta_state[clause*COMPONENTS*TA_CHUNKS_PER_LEAF*STATE_BITS + ta_chunk_base*STATE_BITS];
878
-
879
- for (unsigned long long class_id = 0; class_id < number_of_outputs; ++class_id) {
880
- int local_class_sum = class_sum[class_id];
881
- if (local_class_sum > THRESHOLD) {
882
- local_class_sum = THRESHOLD;
883
- } else if (local_class_sum < -THRESHOLD) {
884
- local_class_sum = -THRESHOLD;
885
- }
886
-
887
- update_component_hierarchy(&localState, number_of_outputs, &clause_weights[class_id*CLAUSES + clause], ta_state, (int) component_output[clause_component], &Xi[feature_chunk_base], y[example*number_of_outputs + class_id], local_class_sum);
888
- }
889
- }
890
-
891
- state[index] = localState;
892
- }
893
-
894
-
895
577
  // Update state of Tsetlin Automata team
896
578
  __global__ void update_weights(curandState *state, int tm_type, int number_of_outputs, int *clause_weights, float *clause_output, float *class_sum, int *y, int example)
897
579
  {
@@ -920,37 +602,13 @@ code_update = """
920
602
 
921
603
  state[index] = localState;
922
604
  }
923
-
924
- // Update state of Tsetlin Automata team
925
- __global__ void update_weights_old(curandState *state, int tm_type, int number_of_outputs, int *clause_weights, float *clause_output, int *class_sum, int *y, int example)
926
- {
927
- int index = blockIdx.x * blockDim.x + threadIdx.x;
928
- int stride = blockDim.x * gridDim.x;
929
-
930
- /* Copy state to local memory for efficiency */
931
- curandState localState = state[index];
932
-
933
- for (unsigned long long clause = index; clause < CLAUSES; clause += stride) {
934
- for (unsigned long long class_id = 0; class_id < number_of_outputs; ++class_id) {
935
- int local_class_sum = class_sum[class_id];
936
- if (local_class_sum > THRESHOLD) {
937
- local_class_sum = THRESHOLD;
938
- } else if (local_class_sum < -THRESHOLD) {
939
- local_class_sum = -THRESHOLD;
940
- }
941
- update_clause_weight(&localState, tm_type, number_of_outputs, &clause_weights[class_id*CLAUSES + clause], (int) clause_output[clause], y[example*number_of_outputs + class_id], local_class_sum);
942
- }
943
- }
944
-
945
- state[index] = localState;
946
- }
947
605
  }
948
606
  """
949
607
 
950
608
  code_prepare = """
951
609
  extern "C"
952
610
  {
953
- __global__ void prepare_weights(curandState *state, int tm_type, int number_of_outputs, int *clause_weights, int *class_sum)
611
+ __global__ void prepare_weights(curandState *state, int tm_type, int number_of_outputs, int *clause_weights)
954
612
  {
955
613
  int index = blockIdx.x * blockDim.x + threadIdx.x;
956
614
  int stride = blockDim.x * gridDim.x;
@@ -974,17 +632,14 @@ code_prepare = """
974
632
  state[index] = localState;
975
633
  }
976
634
 
977
- __global__ void prepare_hierarchy(curandState *state, int number_of_outputs, unsigned int *global_ta_state, int *clause_weights, int *class_sum)
635
+ __global__ void prepare_hierarchy(int number_of_outputs, unsigned int *global_ta_state)
978
636
  {
979
637
  int index = blockIdx.x * blockDim.x + threadIdx.x;
980
638
  int stride = blockDim.x * gridDim.x;
981
639
 
982
- curandState localState = state[index];
983
-
984
- // Evaluate each clause component (leaf) in separate threads
985
- for (int clause_component = index; clause_component < CLAUSES*COMPONENTS; clause_component += stride) {
986
- // Get state of current clause component
987
- unsigned int *ta_state = &global_ta_state[clause_component*TA_CHUNKS_PER_LEAF*STATE_BITS];
640
+ for (int clause_team = index; clause_team < CLAUSES*TA_TEAMS; clause_team += stride) {
641
+ // Get state of current ta team
642
+ unsigned int *ta_state = &global_ta_state[clause_team*TA_CHUNKS_PER_LEAF*STATE_BITS];
988
643
  for (int ta_chunk = 0; ta_chunk < TA_CHUNKS_PER_LEAF; ++ta_chunk) {
989
644
  for (int b = 0; b < STATE_BITS-1; ++b) {
990
645
  ta_state[ta_chunk*STATE_BITS + b] = ~0;
@@ -992,8 +647,6 @@ code_prepare = """
992
647
  ta_state[ta_chunk*STATE_BITS + STATE_BITS - 1] = 0;
993
648
  }
994
649
  }
995
-
996
- state[index] = localState;
997
650
  }
998
651
  }
999
652
  """
@@ -1062,9 +715,9 @@ code_clauses = """
1062
715
  int index = blockIdx.x * blockDim.x + threadIdx.x;
1063
716
  int stride = blockDim.x * gridDim.x;
1064
717
 
1065
- for (unsigned long long i = index; i < 1LLU * CLAUSES * COMPONENTS * LITERALS_PER_LEAF; i += stride) {
1066
- unsigned long long clause = i / (COMPONENTS * LITERALS_PER_LEAF);
1067
- unsigned long long comp = (i / LITERALS_PER_LEAF) % COMPONENTS;
718
+ for (unsigned long long i = index; i < 1LLU * CLAUSES * TA_TEAMS * LITERALS_PER_LEAF; i += stride) {
719
+ unsigned long long clause = i / (TA_TEAMS * LITERALS_PER_LEAF);
720
+ unsigned long long comp = (i / LITERALS_PER_LEAF) % TA_TEAMS;
1068
721
  unsigned long long ta_idx = i % LITERALS_PER_LEAF;
1069
722
 
1070
723
  int chunk = ta_idx / 32;
@@ -1073,14 +726,14 @@ code_clauses = """
1073
726
  unsigned int state = 0;
1074
727
  for (int b = 0; b < STATE_BITS; ++b) {
1075
728
  unsigned int plane = global_ta_state[
1076
- (clause * COMPONENTS * TA_CHUNKS_PER_LEAF * STATE_BITS) +
729
+ (clause * TA_TEAMS * TA_CHUNKS_PER_LEAF * STATE_BITS) +
1077
730
  (comp * TA_CHUNKS_PER_LEAF * STATE_BITS) +
1078
731
  (chunk * STATE_BITS) + b
1079
732
  ];
1080
733
  if (plane & (1U << bit_pos)) state |= (1U << b);
1081
734
  }
1082
735
 
1083
- unpacked_states[(clause * COMPONENTS * LITERALS_PER_LEAF) + (comp * LITERALS_PER_LEAF) + ta_idx] = state;
736
+ unpacked_states[(clause * TA_TEAMS * LITERALS_PER_LEAF) + (comp * LITERALS_PER_LEAF) + ta_idx] = state;
1084
737
  }
1085
738
  }
1086
739
  """
@@ -33,15 +33,15 @@ import sys
33
33
 
34
34
  from time import time
35
35
 
36
- OR_GROUP = " ∨* "
37
- OR_ALTERNATIVES = " ∨ "
38
- AND_GROUP = " ∧ "
36
+ AND_GROUP = 0
37
+ AND_ALTERNATIVES = 1
38
+ OR_GROUP = 2
39
+ OR_ALTERNATIVES = 3
39
40
 
40
41
  VANILLA_TM = 0
41
42
  WEIGHTED_TM = 1
42
43
  COALESCED_TM = 2
43
44
 
44
-
45
45
  class CommonTsetlinMachine():
46
46
 
47
47
  def __init__(self, number_of_clauses, T, s, q=1.0, log_scale=False, 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):
@@ -69,11 +69,8 @@ class CommonTsetlinMachine():
69
69
  self.hierarchy_structure_factors = [0] * (self.depth - 1)
70
70
  self.hierarchy_structure_type = [0] * (self.depth - 1)
71
71
  for d in range(1, self.depth):
72
+ self.hierarchy_structure_type[d-1] = self.hierarchy_structure[d][0]
72
73
  self.hierarchy_structure_factors[d-1] = self.hierarchy_structure[d][1]
73
- if self.hierarchy_structure[d][0] == OR_ALTERNATIVES:
74
- self.hierarchy_structure_type[d-1] = 1
75
- elif self.hierarchy_structure[d][0] == OR_GROUP:
76
- self.hierarchy_structure_type[d-1] = 2
77
74
 
78
75
  # Calculates total number of features spanned by the hierarchy
79
76
  self.number_of_features_hierarchy = 1
@@ -95,10 +92,21 @@ class CommonTsetlinMachine():
95
92
  # Calculates the number of literal chunks for the full hierarchy
96
93
  self.hierarchy_size[0] = self.number_of_literal_chunks_per_leaf * self.hierarchy_size[1]
97
94
 
98
- # Calculates number of literal chunks overall for the feature vector (ignores OR alternatives)
95
+ self.number_of_ta_teams = 1
96
+ for d in range(self.depth - 1):
97
+ if self.hierarchy_structure[d][0] != OR_GROUP: # OR_GROUP reuses the same TA
98
+ self.number_of_ta_teams = self.hierarchy_structure[self.depth - d - 1][1] * self.number_of_ta_teams
99
+
100
+ # Identifies the larget number of OR_GROUP children (for allocating local memory)
101
+ self.max_number_of_or_group_children = 0
102
+ for d in range(1, self.depth):
103
+ if self.hierarchy_structure[d][0] == OR_GROUP and self.max_number_of_or_group_children < self.hierarchy_structure[d][1]:
104
+ self.max_number_of_or_group_children = self.hierarchy_structure[d][1]
105
+
106
+ # Calculates number of literal chunks overall for the feature vector (ignores OR- and AND alternatives)
99
107
  self.number_of_literal_chunks = self.number_of_literal_chunks_per_leaf
100
108
  for d in range(self.depth - 1, 0, -1):
101
- if (self.hierarchy_structure[d][0] == OR_GROUP or self.hierarchy_structure[d][0] == AND_GROUP):
109
+ if (self.hierarchy_structure[d][0] == OR_GROUP or self.hierarchy_structure[d][0] == AND_GROUP): # OR_GROUP and AND_GROUP join the features of their children
102
110
  self.number_of_literal_chunks *= self.hierarchy_structure[d][1]
103
111
 
104
112
  self.np_rng = np.random.default_rng(seed)
@@ -116,6 +124,7 @@ class CommonTsetlinMachine():
116
124
  #define CLAUSES %d
117
125
  #define DEPTH %d
118
126
  #define COMPONENTS %d
127
+ #define TA_TEAMS %d
119
128
  #define LITERALS_PER_LEAF %d
120
129
  #define TA_CHUNKS_PER_LEAF %d
121
130
  #define LITERAL_CHUNKS %d
@@ -126,9 +135,16 @@ class CommonTsetlinMachine():
126
135
  #define Q %f
127
136
  #define LOG_SCALE %d
128
137
 
138
+ #define AND_GROUP %d
139
+ #define AND_ALTERNATIVES %d
140
+ #define OR_GROUP %d
141
+ #define OR_ALTERNATIVES %d
142
+
143
+ #define MAX_NUMBER_OF_OR_GROUP_CHILDREN %d
144
+
129
145
  #define NEGATIVE_CLAUSES %d
130
146
  #define FLIP_POLARITY %d
131
- """ % (self.number_of_clauses, self.depth, self.hierarchy_size[1], self.number_of_literals_per_leaf, self.number_of_literal_chunks_per_leaf, self.number_of_literal_chunks, self.number_of_state_bits, self.boost_true_positive_feedback, self.s, self.T, self.q, self.log_scale, self.negative_clauses, self.flip_polarity)
147
+ """ % (self.number_of_clauses, self.depth, self.hierarchy_size[1], self.number_of_ta_teams, self.number_of_literals_per_leaf, self.number_of_literal_chunks_per_leaf, self.number_of_literal_chunks, self.number_of_state_bits, self.boost_true_positive_feedback, self.s, self.T, self.q, self.log_scale, AND_GROUP, AND_ALTERNATIVES, OR_GROUP, OR_ALTERNATIVES, self.max_number_of_or_group_children, self.negative_clauses, self.flip_polarity)
132
148
 
133
149
  mod_prepare = SourceModule(parameters + kernels.code_header + kernels.code_prepare, no_extern_c=True)
134
150
  self.prepare_weights = mod_prepare.get_function("prepare_weights")
@@ -148,7 +164,7 @@ class CommonTsetlinMachine():
148
164
  self.max_clause_output.prepare("iPP")
149
165
 
150
166
  self.evaluate_final = mod_update.get_function("evaluate_final")
151
- self.evaluate_final.prepare("iPPPP")
167
+ self.evaluate_final.prepare("iPPP")
152
168
 
153
169
  self.rescale_final = mod_update.get_function("rescale_final")
154
170
  self.rescale_final.prepare("iPP")
@@ -186,11 +202,9 @@ class CommonTsetlinMachine():
186
202
 
187
203
  # Prepares for leaf encoding of the input data
188
204
  self.prepare_encode_hierarchy(X_gpu, encoded_X_hierarchy_gpu, np.int32(self.number_of_literal_chunks), np.int32(number_of_examples), grid=self.grid, block=self.block)
189
- cuda.Context.synchronize()
190
205
 
191
206
  # Encodes the input data split across the leaves
192
207
  self.encode_hierarchy(X_gpu, encoded_X_hierarchy_gpu, np.int32(self.number_of_features_hierarchy), np.int32(self.number_of_literal_chunks), np.int32(self.hierarchy_size[1]), np.int32(self.number_of_features_per_leaf), np.int32(self.number_of_literal_chunks_per_leaf), np.int32(self.append_negated), np.int32(number_of_examples), grid=self.grid, block=self.block)
193
- cuda.Context.synchronize()
194
208
 
195
209
  def allocate_gpu_memory(self):
196
210
  # GPU memory for accumulating votes, level by level
@@ -209,7 +223,7 @@ class CommonTsetlinMachine():
209
223
  cuda.memcpy_htod(self.hierarchy_structure_type_gpu, np.array(self.hierarchy_structure_type, dtype=np.int32))
210
224
 
211
225
  # GPU memory for storing Tsetlin Automata states
212
- self.ta_state_hierarchy_gpu = cuda.mem_alloc(self.number_of_clauses*self.hierarchy_size[0]*self.number_of_state_bits*4)
226
+ self.ta_state_hierarchy_gpu = cuda.mem_alloc(self.number_of_clauses*self.number_of_ta_teams*self.number_of_literal_chunks_per_leaf*self.number_of_state_bits*4)
213
227
  self.clause_weights_gpu = cuda.mem_alloc(self.number_of_outputs*self.number_of_clauses*4)
214
228
  self.component_weights_gpu = cuda.mem_alloc(self.number_of_clauses*self.hierarchy_size[1]*4) # Only positive weights...
215
229
  self.class_sum_gpu = cuda.mem_alloc(self.number_of_outputs*4)
@@ -218,16 +232,16 @@ class CommonTsetlinMachine():
218
232
  self.clause_output_max = np.ascontiguousarray(np.empty(1)).astype(np.float32)
219
233
 
220
234
  def ta_action(self, clause, leaf, ta):
221
- ta_state_hierarchy = np.empty(self.number_of_clauses*self.hierarchy_size[1]*self.number_of_literal_chunks_per_leaf*self.number_of_state_bits, dtype=np.uint32)
235
+ ta_state_hierarchy = np.empty(self.number_of_clauses*self.number_of_ta_teams*self.number_of_literal_chunks_per_leaf*self.number_of_state_bits, dtype=np.uint32)
222
236
  cuda.memcpy_dtoh(ta_state_hierarchy, self.ta_state_hierarchy_gpu)
223
- ta_state_hierarchy = ta_state_hierarchy.reshape((self.number_of_clauses, self.hierarchy_size[1], self.number_of_literal_chunks_per_leaf, self.number_of_state_bits))
237
+ ta_state_hierarchy = ta_state_hierarchy.reshape((self.number_of_clauses, self.number_of_ta_teams, self.number_of_literal_chunks_per_leaf, self.number_of_state_bits))
224
238
 
225
239
  return (ta_state_hierarchy[clause, leaf, ta // 32, self.number_of_state_bits-1] & (1 << (ta % 32))) > 0
226
240
 
227
241
  def ta_state(self, clause, leaf, ta):
228
- ta_state_hierarchy = np.empty(self.number_of_clauses*self.hierarchy_size[1]*self.number_of_literal_chunks_per_leaf*self.number_of_state_bits, dtype=np.uint32)
242
+ ta_state_hierarchy = np.empty(self.number_of_clauses*self.number_of_ta_teams*self.number_of_literal_chunks_per_leaf*self.number_of_state_bits, dtype=np.uint32)
229
243
  cuda.memcpy_dtoh(ta_state_hierarchy, self.ta_state_hierarchy_gpu)
230
- ta_state_hierarchy = ta_state_hierarchy.reshape((self.number_of_clauses, self.hierarchy_size[1], self.number_of_literal_chunks_per_leaf, self.number_of_state_bits))
244
+ ta_state_hierarchy = ta_state_hierarchy.reshape((self.number_of_clauses, self.number_of_ta_teams, self.number_of_literal_chunks_per_leaf, self.number_of_state_bits))
231
245
 
232
246
  state = 0
233
247
  for b in range(self.number_of_state_bits):
@@ -238,7 +252,7 @@ class CommonTsetlinMachine():
238
252
 
239
253
  def get_state(self):
240
254
  # To be updated
241
- ta_state_hierarchy = np.empty(self.number_of_clauses*self.hierarchy_size[1]*self.number_of_literal_chunks_per_leaf*self.number_of_state_bits, dtype=np.uint32)
255
+ ta_state_hierarchy = np.empty(self.number_of_clauses*self.number_of_ta_teams*self.number_of_literal_chunks_per_leaf*self.number_of_state_bits, dtype=np.uint32)
242
256
  cuda.memcpy_dtoh(self.ta_state_hierarchy, self.ta_state_hierarchy_gpu)
243
257
  clause_weights = np.empty(self.number_of_outputs*self.number_of_clauses).astype(np.int32)
244
258
  cuda.memcpy_dtoh(self.clause_weights, self.clause_weights_gpu)
@@ -255,7 +269,7 @@ class CommonTsetlinMachine():
255
269
  self.min_y = state[8]
256
270
  self.max_y = state[9]
257
271
 
258
- self.ta_state_hierarchy_gpu = cuda.mem_alloc(self.number_of_clauses*self.hierarchy_size[0]*self.number_of_state_bits*4)
272
+ self.ta_state_hierarchy_gpu = cuda.mem_alloc(self.number_of_clauses*self.number_of_ta_teams*self.number_of_state_bits*4)
259
273
  self.clause_weights_gpu = cuda.mem_alloc(self.number_of_outputs*self.number_of_clauses*4)
260
274
  cuda.memcpy_htod(self.ta_state_hierarchy_gpu, state[0])
261
275
  cuda.memcpy_htod(self.clause_weights_gpu, state[1])
@@ -265,15 +279,13 @@ class CommonTsetlinMachine():
265
279
  None # To be updated
266
280
 
267
281
  def initialize_weights_and_ta_states(self):
268
- 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)
269
- cuda.Context.synchronize()
282
+ self.prepare_weights(self.cuda_rng.state, np.int32(self.tm_type), np.int32(self.number_of_outputs), self.clause_weights_gpu, grid=self.grid, block=self.block)
270
283
 
271
- 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)
272
- cuda.Context.synchronize()
284
+ self.prepare_hierarchy(np.int32(self.number_of_outputs), self.ta_state_hierarchy_gpu, grid=self.grid, block=self.block)
273
285
 
274
286
  def evaluate_hierarchy(self, encoded_X_hierarchy, e):
275
287
  # Initializes class sums to zero
276
- class_sum = np.ascontiguousarray(np.zeros(self.number_of_outputs)).astype(np.int32)
288
+ class_sum = np.zeros(self.number_of_outputs, dtype=np.float32)
277
289
  cuda.memcpy_htod(self.class_sum_gpu, class_sum)
278
290
 
279
291
  # Evaluates all the hierarchy leaves in parallel
@@ -289,11 +301,10 @@ class CommonTsetlinMachine():
289
301
  encoded_X_hierarchy,
290
302
  np.int32(e)
291
303
  )
292
- cuda.Context.synchronize()
293
304
 
294
305
  # Propagates votes bottom-up in the hierarchy, starting from the clause components (leaves)
295
306
  for d in range(1, self.depth):
296
- if (self.hierarchy_structure[d][0] == AND_GROUP):
307
+ if (self.hierarchy_structure[d][0] == AND_GROUP or self.hierarchy_structure[d][0] == AND_ALTERNATIVES):
297
308
  self.evaluate_and_groups.prepared_call(
298
309
  self.grid,
299
310
  self.block,
@@ -302,7 +313,6 @@ class CommonTsetlinMachine():
302
313
  self.hierarchy_size[d + 1],
303
314
  self.hierarchy_structure[d][1]
304
315
  )
305
- cuda.Context.synchronize()
306
316
  elif self.hierarchy_structure[d][0] == OR_GROUP:
307
317
  self.evaluate_or_groups.prepared_call(
308
318
  self.grid,
@@ -312,7 +322,6 @@ class CommonTsetlinMachine():
312
322
  self.hierarchy_size[d + 1],
313
323
  self.hierarchy_structure[d][1]
314
324
  )
315
- cuda.Context.synchronize()
316
325
  elif self.hierarchy_structure[d][0] == OR_ALTERNATIVES:
317
326
  self.evaluate_or_alternatives.prepared_call(
318
327
  self.grid,
@@ -322,23 +331,20 @@ class CommonTsetlinMachine():
322
331
  self.hierarchy_size[d + 1],
323
332
  self.hierarchy_structure[d][1]
324
333
  )
325
- cuda.Context.synchronize()
326
334
  else:
327
- printf("Unknown node type!")
328
- sys.exit()
335
+ raise ValueError("Unknown Node Type!")
329
336
 
330
337
  self.clause_output_max[:] = np.finfo(np.float32).min
331
338
  cuda.memcpy_htod(self.clause_output_max_gpu, self.clause_output_max)
332
339
 
333
- if self.log_scale:
334
- self.max_clause_output.prepared_call(
335
- self.grid,
336
- self.block,
337
- np.int32(self.number_of_outputs),
338
- self.hierarchy_votes[self.depth-1],
339
- self.clause_output_max_gpu
340
- )
341
- cuda.Context.synchronize()
340
+ # if self.log_scale:
341
+ # self.max_clause_output.prepared_call(
342
+ # self.grid,
343
+ # self.block,
344
+ # np.int32(self.number_of_outputs),
345
+ # self.hierarchy_votes[self.depth-1],
346
+ # self.clause_output_max_gpu
347
+ # )
342
348
 
343
349
  # Adds up the votes from each clause (hierarchy root)
344
350
  self.evaluate_final.prepared_call(
@@ -346,23 +352,20 @@ class CommonTsetlinMachine():
346
352
  self.block,
347
353
  np.int32(self.number_of_outputs),
348
354
  self.hierarchy_votes[self.depth-1],
349
- self.clause_output_max_gpu,
350
355
  self.clause_weights_gpu,
351
356
  self.class_sum_gpu
352
357
  )
353
- cuda.Context.synchronize()
354
358
 
355
- if self.log_scale:
356
- self.rescale_final.prepared_call(
357
- self.grid,
358
- self.block,
359
- np.int32(self.number_of_outputs),
360
- self.clause_output_max_gpu,
361
- self.class_sum_gpu
362
- )
363
- cuda.Context.synchronize()
359
+ # if self.log_scale:
360
+ # self.rescale_final.prepared_call(
361
+ # self.grid,
362
+ # self.block,
363
+ # np.int32(self.number_of_outputs),
364
+ # self.clause_output_max_gpu,
365
+ # self.class_sum_gpu
366
+ # )
364
367
 
365
- def _fit(self, X, encoded_Y, epochs=100, incremental=False):
368
+ def _fit(self, X, encoded_Y):
366
369
  if self.number_of_features_hierarchy != X.shape[1]:
367
370
  print("The number of features spanned by hierarchy does not align with the input data.")
368
371
  sys.exit(-1)
@@ -376,10 +379,7 @@ class CommonTsetlinMachine():
376
379
  self.initialize_weights_and_ta_states()
377
380
 
378
381
  self.first = False
379
- elif not incremental:
380
- # Re-initializes weights and Tsetlin automata states if training is not incremental
381
- self.initialize_weights_and_ta_states()
382
-
382
+
383
383
  # Allocates GPU memory for training data
384
384
  encoded_X_hierarchy_training_gpu = cuda.mem_alloc(int(number_of_examples * self.number_of_literal_chunks * 4))
385
385
  Y_gpu = cuda.mem_alloc(encoded_Y.nbytes)
@@ -387,69 +387,64 @@ class CommonTsetlinMachine():
387
387
  self.encode_X(X, encoded_X_hierarchy_training_gpu)
388
388
  cuda.memcpy_htod(Y_gpu, encoded_Y)
389
389
 
390
- for epoch in range(epochs):
391
- for e in range(number_of_examples):
392
- self.evaluate_hierarchy(encoded_X_hierarchy_training_gpu, e)
393
-
394
- # Propagates the root value and any intermittent node values back to the leaves.
395
- # The purpose is to determine which leaves only has True nodes on the path from leaf to root.
396
- for d in range(self.depth-1, 0, -1):
397
- if self.hierarchy_structure[d][0] != OR_GROUP:
398
- self.propagate_and_group_false_truth_values.prepared_call(
399
- self.grid,
400
- self.block,
401
- self.hierarchy_votes[d-1],
402
- self.hierarchy_votes[d],
403
- self.hierarchy_size[d + 1],
404
- self.hierarchy_structure[d][1]
405
- )
406
- cuda.Context.synchronize()
407
- else:
408
- self.propagate_or_group_false_truth_values.prepared_call(
409
- self.grid,
410
- self.block,
411
- self.cuda_rng.state,
412
- self.hierarchy_votes[d-1],
413
- self.hierarchy_votes[d],
414
- self.hierarchy_size[d + 1],
415
- self.hierarchy_structure[d][1]
416
- )
417
- cuda.Context.synchronize()
418
-
419
- # Updates the clause components (leaves) based on the propagated truth values
420
- self.update_hierarchy.prepared_call(
390
+ for e in range(number_of_examples):
391
+ self.evaluate_hierarchy(encoded_X_hierarchy_training_gpu, e)
392
+
393
+ # Propagates the root value and any intermittent node values back to the leaves.
394
+ # The purpose is to determine which leaves only has True nodes on the path from leaf to root.
395
+ for d in range(self.depth-1, 0, -1):
396
+ if self.hierarchy_structure[d][0] != OR_GROUP:
397
+ self.propagate_and_group_false_truth_values.prepared_call(
398
+ self.grid,
399
+ self.block,
400
+ self.hierarchy_votes[d-1],
401
+ self.hierarchy_votes[d],
402
+ self.hierarchy_size[d + 1],
403
+ self.hierarchy_structure[d][1]
404
+ )
405
+ else:
406
+ self.propagate_or_group_false_truth_values.prepared_call(
407
+ self.grid,
408
+ self.block,
409
+ self.cuda_rng.state,
410
+ self.hierarchy_votes[d-1],
411
+ self.hierarchy_votes[d],
412
+ self.hierarchy_size[d + 1],
413
+ self.hierarchy_structure[d][1]
414
+ )
415
+
416
+ # Updates the clause components (leaves) based on the propagated truth values
417
+ self.update_hierarchy.prepared_call(
418
+ self.grid,
419
+ self.block,
420
+ self.cuda_rng.state,
421
+ np.int32(self.number_of_outputs),
422
+ self.ta_state_hierarchy_gpu,
423
+ self.clause_weights_gpu,
424
+ self.hierarchy_votes[0],
425
+ self.depth,
426
+ self.hierarchy_structure_factors_gpu,
427
+ self.hierarchy_structure_type_gpu,
428
+ self.class_sum_gpu,
429
+ encoded_X_hierarchy_training_gpu,
430
+ Y_gpu,
431
+ np.int32(e)
432
+ )
433
+
434
+ # Updates the clause weights
435
+ if (self.tm_type in [WEIGHTED_TM, COALESCED_TM]):
436
+ self.update_weights.prepared_call(
421
437
  self.grid,
422
438
  self.block,
423
439
  self.cuda_rng.state,
440
+ np.int32(self.tm_type),
424
441
  np.int32(self.number_of_outputs),
425
- self.ta_state_hierarchy_gpu,
426
442
  self.clause_weights_gpu,
427
- self.hierarchy_votes[0],
428
- self.depth,
429
- self.hierarchy_structure_factors_gpu,
430
- self.hierarchy_structure_type_gpu,
443
+ self.hierarchy_votes[self.depth-1],
431
444
  self.class_sum_gpu,
432
- encoded_X_hierarchy_training_gpu,
433
445
  Y_gpu,
434
446
  np.int32(e)
435
447
  )
436
- cuda.Context.synchronize()
437
-
438
- # Updates the clause weights
439
- if (self.tm_type in [WEIGHTED_TM, COALESCED_TM]):
440
- self.update_weights.prepared_call(
441
- self.grid,
442
- self.block,
443
- self.cuda_rng.state,
444
- np.int32(self.tm_type),
445
- np.int32(self.number_of_outputs),
446
- self.clause_weights_gpu,
447
- self.hierarchy_votes[self.depth-1],
448
- self.class_sum_gpu,
449
- Y_gpu,
450
- np.int32(e)
451
- )
452
- cuda.Context.synchronize()
453
448
  return
454
449
 
455
450
  def _score(self, X):
@@ -543,8 +538,8 @@ class CommonTsetlinMachine():
543
538
  encoded_X_hierarchy_test_gpu = gpuarray.empty((number_of_examples, self.number_of_literal_chunks), dtype=np.uint32)
544
539
  self.encode_X(X, encoded_X_hierarchy_test_gpu.gpudata)
545
540
 
546
- class_sum = np.ascontiguousarray(np.zeros((self.number_of_outputs, number_of_examples))).astype(np.int32)
547
- class_sum_example = np.ascontiguousarray(np.zeros(self.number_of_outputs)).astype(np.int32)
541
+ class_sum = np.zeros((self.number_of_outputs, number_of_examples), dtype=np.float32)
542
+ class_sum_example = np.zeros(self.number_of_outputs, dtype=np.float32)
548
543
  hierarchy_votes = []
549
544
  for e in range(number_of_examples):
550
545
  self.evaluate_hierarchy(encoded_X_hierarchy_test_gpu.gpudata, e)
@@ -554,7 +549,7 @@ class CommonTsetlinMachine():
554
549
 
555
550
  hierarchy_votes_example = []
556
551
  for d in range(self.depth):
557
- temp_arr = np.empty(self.number_of_clauses*int(self.hierarchy_size[d+1]), dtype=np.int32)
552
+ temp_arr = np.zeros(self.number_of_clauses*int(self.hierarchy_size[d+1]), dtype=np.float32)
558
553
  cuda.memcpy_dtoh(temp_arr, self.hierarchy_votes[d])
559
554
  hierarchy_votes_example.append(temp_arr.reshape((self.number_of_clauses, int(self.hierarchy_size[d+1]))))
560
555
 
@@ -581,7 +576,7 @@ class CommonTsetlinMachine():
581
576
  depth_d_node_index = component_remainder % self.hierarchy_structure[d][1]
582
577
  component_remainder = component_remainder // self.hierarchy_structure[d][1]
583
578
 
584
- if self.hierarchy_structure[d][0] != OR_ALTERNATIVES:
579
+ if (self.hierarchy_structure[d][0] != OR_ALTERNATIVES) and (self.hierarchy_structure[d][0] != AND_ALTERNATIVES):
585
580
  feature_base += size * depth_d_node_index
586
581
  size *= self.hierarchy_structure[d][1];
587
582
 
@@ -591,7 +586,10 @@ class CommonTsetlinMachine():
591
586
  right.append(")")
592
587
  left.insert(0, "(")
593
588
  elif previous_index[d-1] != depth_d_node_index:
594
- inside.append(self.hierarchy_structure[d][0])
589
+ if self.hierarchy_structure[d][0] in [AND_GROUP, AND_ALTERNATIVES]:
590
+ inside.append(" ∧ ")
591
+ elif self.hierarchy_structure[d][0] in [OR_GROUP, OR_ALTERNATIVES]:
592
+ inside.append(" ∨ ")
595
593
 
596
594
  previous_index[d-1] = depth_d_node_index
597
595
 
@@ -607,7 +605,7 @@ class CommonTsetlinMachine():
607
605
  l = []
608
606
  for k in range(self.number_of_literals_per_leaf):
609
607
  if self.ta_action(i, j, k):
610
- if k < self.number_of_literals_per_leaf // 2:
608
+ if (not self.append_negated) or k < self.number_of_literals_per_leaf // 2:
611
609
  if print_ta_state:
612
610
  l.append("x%d(%d)" % (feature_base + k, self.ta_state(i, j, k)))
613
611
  else:
@@ -672,7 +670,7 @@ class MultiOutputTsetlinMachine(CommonTsetlinMachine):
672
670
  self.negative_clauses = 1
673
671
  super().__init__(number_of_clauses, T, s, q=q, log_scale=log_scale, 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)
674
672
 
675
- def fit(self, X, Y, epochs=100, incremental=False):
673
+ def fit(self, X, Y):
676
674
  X = X.reshape(X.shape[0], X.shape[1], 1)
677
675
 
678
676
  self.number_of_outputs = Y.shape[1]
@@ -682,7 +680,7 @@ class MultiOutputTsetlinMachine(CommonTsetlinMachine):
682
680
  self.min_y = None
683
681
 
684
682
  encoded_Y = np.where(Y == 1, self.T, -self.T).astype(np.int32)
685
- self._fit(X, encoded_Y, epochs = epochs, incremental = incremental)
683
+ self._fit(X, encoded_Y)
686
684
 
687
685
  return
688
686
 
@@ -701,7 +699,7 @@ class MultiClassCoalescedTsetlinMachine(CommonTsetlinMachine):
701
699
 
702
700
  super().__init__(number_of_clauses, T, s, q=q, log_scale=log_scale, 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)
703
701
 
704
- def fit(self, X, Y, epochs=100, incremental=False):
702
+ def fit(self, X, Y):
705
703
  X = X.reshape(X.shape[0], X.shape[1], 1)
706
704
 
707
705
  self.number_of_outputs = int(np.max(Y) + 1)
@@ -714,7 +712,7 @@ class MultiClassCoalescedTsetlinMachine(CommonTsetlinMachine):
714
712
  for i in range(self.number_of_outputs):
715
713
  encoded_Y[:,i] = np.where(Y == i, self.T, -self.T)
716
714
 
717
- self._fit(X, encoded_Y, epochs = epochs, incremental = incremental)
715
+ self._fit(X, encoded_Y)
718
716
 
719
717
  return
720
718
 
@@ -743,7 +741,7 @@ class MultiClassTsetlinMachine:
743
741
 
744
742
  self.configured = False
745
743
 
746
- def fit(self, X, Y, epochs=100, incremental=False):
744
+ def fit(self, X, Y):
747
745
  self.number_of_outputs = int(np.max(Y) + 1)
748
746
 
749
747
  if not self.configured:
@@ -755,19 +753,18 @@ class MultiClassTsetlinMachine:
755
753
 
756
754
  encoded_Y = np.empty(Y.shape[0], dtype = np.int32)
757
755
 
758
- for epoch in range(epochs):
759
- for i in range(self.number_of_outputs):
760
- target_X = X[Y==i]
756
+ for i in range(self.number_of_outputs):
757
+ target_X = X[Y==i]
761
758
 
762
- not_target_X = X[Y!=i]
763
- not_target_index = np.random.rand(not_target_X.shape[0]) <= 1.0/(self.number_of_outputs - 1)
759
+ not_target_X = X[Y!=i]
760
+ not_target_index = np.random.rand(not_target_X.shape[0]) <= 1.0/(self.number_of_outputs - 1)
764
761
 
765
- balanced_X = np.vstack((target_X, not_target_X[not_target_index,:]))
766
- balanced_Y = np.hstack((np.ones(target_X.shape[0]), np.zeros(not_target_X.shape[0])))
767
- index = np.arange(balanced_X.shape[0])
768
- np.random.shuffle(index)
762
+ balanced_X = np.vstack((target_X, not_target_X[not_target_index,:]))
763
+ balanced_Y = np.hstack((np.ones(target_X.shape[0]), np.zeros(not_target_X.shape[0])))
764
+ index = np.arange(balanced_X.shape[0])
765
+ np.random.shuffle(index)
769
766
 
770
- self.tms[i].fit(balanced_X[index], balanced_Y[index], epochs=1, incremental=incremental)
767
+ self.tms[i].fit(balanced_X[index], balanced_Y[index])
771
768
  return
772
769
 
773
770
  def score(self, X):
@@ -799,6 +796,11 @@ class MultiClassTsetlinMachine:
799
796
  },
800
797
  }
801
798
 
799
+ def print_hierarchy(self, print_ta_state=False):
800
+ for i in range(self.number_of_outputs):
801
+ print("CLASS %d\n", i)
802
+ self.tms[i].print_hierarchy(print_ta_state = print_ta_state)
803
+
802
804
  def load(self, state_dict: dict):
803
805
  self.number_of_outputs = state_dict['number_of_outputs']
804
806
 
@@ -822,7 +824,7 @@ class TsetlinMachine(CommonTsetlinMachine):
822
824
 
823
825
  super().__init__(number_of_clauses, T, s, q=q, log_scale=log_scale, 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)
824
826
 
825
- def fit(self, X, Y, epochs=100, incremental=False):
827
+ def fit(self, X, Y):
826
828
  X = X.reshape(X.shape[0], X.shape[1], 1)
827
829
 
828
830
  self.number_of_outputs = 1
@@ -832,7 +834,7 @@ class TsetlinMachine(CommonTsetlinMachine):
832
834
 
833
835
  encoded_Y = np.where(Y == 1, self.T, -self.T).astype(np.int32)
834
836
 
835
- self._fit(X, encoded_Y, epochs = epochs, incremental = incremental)
837
+ self._fit(X, encoded_Y)
836
838
 
837
839
  return
838
840
 
@@ -850,7 +852,7 @@ class RegressionTsetlinMachine(CommonTsetlinMachine):
850
852
 
851
853
  super().__init__(number_of_clauses, T, s, log_scale=log_scale, 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)
852
854
 
853
- def fit(self, X, Y, epochs=100, incremental=False):
855
+ def fit(self, X, Y):
854
856
  X = X.reshape(X.shape[0], X.shape[1], 1)
855
857
 
856
858
  self.number_of_outputs = 1
@@ -861,7 +863,7 @@ class RegressionTsetlinMachine(CommonTsetlinMachine):
861
863
 
862
864
  encoded_Y = ((Y - self.min_y)/(self.max_y - self.min_y)*self.T).astype(np.int32)
863
865
 
864
- self._fit(X, encoded_Y, epochs = epochs, incremental = incremental)
866
+ self._fit(X, encoded_Y)
865
867
 
866
868
  return
867
869
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PyHierarchicalTsetlinMachineCUDA
3
- Version: 0.2.8
3
+ Version: 0.3.0
4
4
  Summary: Hierarchical Tsetlin Machine Architecture.
5
5
  Home-page: https://github.com/cair/PyHierarchicalTsetlinMachineCUDA/
6
6
  Author: Ole-Christoffer Granmo
@@ -11,7 +11,7 @@ or
11
11
  git clone git@github.com:cair/PyHierarchicalTsetlinMachineCUDA.git
12
12
  cd PyHierarchicalTsetlinMachineCUDA
13
13
  python ./setup.py sdist
14
- pip install dist/PyHierarchicalTsetlinMachineCUDA-0.2.6.tar.gz
14
+ pip install dist/PyHierarchicalTsetlinMachineCUDA-0.2.8.tar.gz
15
15
  ```
16
16
 
17
17
  ## Examples
@@ -2,7 +2,7 @@ from setuptools import *
2
2
 
3
3
  setup(
4
4
  name='PyHierarchicalTsetlinMachineCUDA',
5
- version='0.2.8',
5
+ version='0.3.0',
6
6
  author='Ole-Christoffer Granmo',
7
7
  author_email='ole.granmo@uia.no',
8
8
  url='https://github.com/cair/PyHierarchicalTsetlinMachineCUDA/',