PyHierarchicalTsetlinMachineCUDA 0.3.1__tar.gz → 0.3.2__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.3.1 → PyHierarchicalTsetlinMachineCUDA-0.3.2}/PKG-INFO +1 -1
  2. {PyHierarchicalTsetlinMachineCUDA-0.3.1 → PyHierarchicalTsetlinMachineCUDA-0.3.2}/PyHierarchicalTsetlinMachineCUDA/kernels.py +62 -153
  3. {PyHierarchicalTsetlinMachineCUDA-0.3.1 → PyHierarchicalTsetlinMachineCUDA-0.3.2}/PyHierarchicalTsetlinMachineCUDA/tm.py +37 -44
  4. {PyHierarchicalTsetlinMachineCUDA-0.3.1 → PyHierarchicalTsetlinMachineCUDA-0.3.2}/PyHierarchicalTsetlinMachineCUDA/utils.py +30 -8
  5. {PyHierarchicalTsetlinMachineCUDA-0.3.1 → PyHierarchicalTsetlinMachineCUDA-0.3.2}/PyHierarchicalTsetlinMachineCUDA.egg-info/PKG-INFO +1 -1
  6. {PyHierarchicalTsetlinMachineCUDA-0.3.1 → PyHierarchicalTsetlinMachineCUDA-0.3.2}/README.md +2 -2
  7. {PyHierarchicalTsetlinMachineCUDA-0.3.1 → PyHierarchicalTsetlinMachineCUDA-0.3.2}/setup.py +1 -1
  8. {PyHierarchicalTsetlinMachineCUDA-0.3.1 → PyHierarchicalTsetlinMachineCUDA-0.3.2}/LICENSE +0 -0
  9. {PyHierarchicalTsetlinMachineCUDA-0.3.1 → PyHierarchicalTsetlinMachineCUDA-0.3.2}/PyHierarchicalTsetlinMachineCUDA/__init__.py +0 -0
  10. {PyHierarchicalTsetlinMachineCUDA-0.3.1 → PyHierarchicalTsetlinMachineCUDA-0.3.2}/PyHierarchicalTsetlinMachineCUDA/tools.py +0 -0
  11. {PyHierarchicalTsetlinMachineCUDA-0.3.1 → PyHierarchicalTsetlinMachineCUDA-0.3.2}/PyHierarchicalTsetlinMachineCUDA.egg-info/SOURCES.txt +0 -0
  12. {PyHierarchicalTsetlinMachineCUDA-0.3.1 → PyHierarchicalTsetlinMachineCUDA-0.3.2}/PyHierarchicalTsetlinMachineCUDA.egg-info/dependency_links.txt +0 -0
  13. {PyHierarchicalTsetlinMachineCUDA-0.3.1 → PyHierarchicalTsetlinMachineCUDA-0.3.2}/PyHierarchicalTsetlinMachineCUDA.egg-info/requires.txt +0 -0
  14. {PyHierarchicalTsetlinMachineCUDA-0.3.1 → PyHierarchicalTsetlinMachineCUDA-0.3.2}/PyHierarchicalTsetlinMachineCUDA.egg-info/top_level.txt +0 -0
  15. {PyHierarchicalTsetlinMachineCUDA-0.3.1 → PyHierarchicalTsetlinMachineCUDA-0.3.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PyHierarchicalTsetlinMachineCUDA
3
- Version: 0.3.1
3
+ Version: 0.3.2
4
4
  Summary: Hierarchical Tsetlin Machine Architecture.
5
5
  Home-page: https://github.com/cair/PyHierarchicalTsetlinMachineCUDA/
6
6
  Author: Ole-Christoffer Granmo
@@ -94,8 +94,14 @@ code_update = """
94
94
 
95
95
  int sign = (*clause_weight >= 0) - (*clause_weight < 0);
96
96
 
97
- float absolute_prediction_error = fabsf(y - class_sum);
98
- if (curand_uniform(localState) <= 1.0*absolute_prediction_error/(2*THRESHOLD)) {
97
+ float update_probability;
98
+ #if CONSTANT_UPDATE_P == 1
99
+ update_probability = 1.0;
100
+ #else
101
+ update_probability = fabsf(y - class_sum)/(2*THRESHOLD);
102
+ #endif
103
+
104
+ if (curand_uniform(localState) <= update_probability) {
99
105
  if (target*sign > 0) {
100
106
  if (clause_output && abs(*clause_weight) < INT_MAX) {
101
107
  (*clause_weight) += sign;
@@ -124,8 +130,14 @@ code_update = """
124
130
 
125
131
  int sign = (*clause_weight >= 0) - (*clause_weight < 0);
126
132
 
127
- float absolute_prediction_error = fabsf(y - class_sum);
128
- if (curand_uniform(localState) <= 1.0*absolute_prediction_error/(2*THRESHOLD)) {
133
+ float update_probability;
134
+ #if CONSTANT_UPDATE_P == 1
135
+ update_probability = 1.0;
136
+ #else
137
+ update_probability = fabsf(y - class_sum)/(2*THRESHOLD);
138
+ #endif
139
+
140
+ if (curand_uniform(localState) <= update_probability) {
129
141
  if (target*sign > 0) {
130
142
  // Type I Feedback
131
143
  for (int ta_chunk = 0; ta_chunk < TA_CHUNKS_PER_LEAF; ++ta_chunk) {
@@ -199,31 +211,19 @@ code_update = """
199
211
 
200
212
  // Evaluate clause component
201
213
 
202
- #if LOG_SCALE == 1
203
- float component_output = 0;
204
- #else
205
- float component_output = 1;
206
- #endif
214
+ float component_output = 1;
207
215
 
208
216
  for (int ta_chunk = 0; ta_chunk < TA_CHUNKS_PER_LEAF-1; ++ta_chunk) {
209
217
  // Compare the TA state of the component (leaf) against the corresponding part of the feature vector
210
218
  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]) {
211
- #if LOG_SCALE == 1
212
- component_output = NEG_INFINITY;
213
- #else
214
- component_output = 0;
215
- #endif
219
+ component_output = 0;
216
220
 
217
221
  break;
218
222
  }
219
223
  }
220
224
 
221
225
  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)) {
222
- #if LOG_SCALE == 1
223
- component_output = NEG_INFINITY;
224
- #else
225
- component_output = 0;
226
- #endif
226
+ component_output = 0;
227
227
  }
228
228
 
229
229
  global_component_output[clause_component] = component_output;
@@ -262,17 +262,12 @@ code_update = """
262
262
  for (int and_group_node = index; and_group_node < CLAUSES*number_of_and_group_nodes; and_group_node += stride) {
263
263
  // Multiply and factors
264
264
 
265
- #if LOG_SCALE == 1
266
- float and_group_vote_product = 0;
267
- #else
268
- float and_group_vote_product = 1;
269
- #endif
270
-
265
+ float and_group_vote_product = 1;
271
266
  for (int and_factor = 0; and_factor < number_of_and_group_factors; ++and_factor) {
272
267
  // Aggregate votes from each child node through multiplication
273
268
 
274
- #if LOG_SCALE == 1
275
- and_group_vote_product += child_input[and_group_node*number_of_and_group_factors + and_factor];
269
+ #if AND_GROUP_NORMALIZATION == 1
270
+ and_group_vote_product *= powf(child_input[and_group_node*number_of_and_group_factors + and_factor], 1.0/number_of_and_group_factors);
276
271
  #else
277
272
  and_group_vote_product *= child_input[and_group_node*number_of_and_group_factors + and_factor];
278
273
  #endif
@@ -289,24 +284,14 @@ code_update = """
289
284
  int stride = blockDim.x * gridDim.x;
290
285
 
291
286
  // If a group node is false, all children are made false.
292
- for (int group_node = index; group_node < CLAUSES*number_of_group_nodes; group_node += stride) {
293
- #if LOG_SCALE == 1
294
- if (group_node_output[group_node] == NEG_INFINITY) {
295
- for (int and_factor = 0; and_factor < number_of_group_node_children; ++and_factor) {
296
- if (child_input[group_node*number_of_group_node_children + and_factor] >= 0) {
297
- child_input[group_node*number_of_group_node_children + and_factor] = NEG_INFINITY;
298
- }
299
- }
300
- }
301
- #else
302
- if (group_node_output[group_node] == 0) {
303
- for (int and_factor = 0; and_factor < number_of_group_node_children; ++and_factor) {
304
- if (child_input[group_node*number_of_group_node_children + and_factor] > 0) {
305
- child_input[group_node*number_of_group_node_children + and_factor] = 0;
306
- }
287
+ for (int group_node = index; group_node < CLAUSES*number_of_group_nodes; group_node += stride) {
288
+ if (group_node_output[group_node] == 0) {
289
+ for (int and_factor = 0; and_factor < number_of_group_node_children; ++and_factor) {
290
+ if (child_input[group_node*number_of_group_node_children + and_factor] > 0) {
291
+ child_input[group_node*number_of_group_node_children + and_factor] = 0;
307
292
  }
308
293
  }
309
- #endif
294
+ }
310
295
 
311
296
  if (group_node_output[group_node] == -1) {
312
297
  for (int and_factor = 0; and_factor < number_of_group_node_children; ++and_factor) {
@@ -328,50 +313,29 @@ code_update = """
328
313
 
329
314
  // If a group node is false, all children are made false.
330
315
  for (int group_node = index; group_node < CLAUSES*number_of_group_nodes; group_node += stride) {
331
- #if LOG_SCALE == 1
332
- if (group_node_output[group_node] == -1) {
333
- for (int or_addend = 0; or_addend < number_of_group_node_children; ++or_addend) {
334
- child_input[group_node*number_of_group_node_children + or_addend] = -1;
335
- }
336
- } else if (group_node_output[group_node] == NEG_INFINITY) {
337
- for (int or_addend = 0; or_addend < number_of_group_node_children; ++or_addend) {
338
- if (child_input[group_node*number_of_group_node_children + or_addend] >= 0) {
339
- child_input[group_node*number_of_group_node_children + or_addend] = NEG_INFINITY;
340
- }
341
- }
342
- } else {
343
- child_input_sum = 0;
344
- for (int or_addend = 0; or_addend < number_of_group_node_children; ++or_addend) {
345
- if (child_input[group_node*number_of_group_node_children + or_addend] >= 0) {
346
- child_input_sum += np.exp2f(child_input[group_node*number_of_group_node_children + or_addend]); // Needs normalization
347
- }
348
- }
316
+ if (group_node_output[group_node] == -1) {
317
+ for (int or_addend = 0; or_addend < number_of_group_node_children; ++or_addend) {
318
+ child_input[group_node*number_of_group_node_children + or_addend] = -1;
349
319
  }
350
- #else
351
- if (group_node_output[group_node] == -1) {
352
- for (int or_addend = 0; or_addend < number_of_group_node_children; ++or_addend) {
353
- child_input[group_node*number_of_group_node_children + or_addend] = -1;
354
- }
355
- } else if (group_node_output[group_node] == 0) {
356
- for (int or_addend = 0; or_addend < number_of_group_node_children; ++or_addend) {
357
- if (child_input[group_node*number_of_group_node_children + or_addend] > 0) {
358
- child_input[group_node*number_of_group_node_children + or_addend] = 0;
359
- }
320
+ } else if (group_node_output[group_node] == 0) {
321
+ for (int or_addend = 0; or_addend < number_of_group_node_children; ++or_addend) {
322
+ if (child_input[group_node*number_of_group_node_children + or_addend] > 0) {
323
+ child_input[group_node*number_of_group_node_children + or_addend] = 0;
360
324
  }
361
- } else {
362
- child_input_sum = 0;
363
- for (int or_addend = 0; or_addend < number_of_group_node_children; ++or_addend) {
364
- if (child_input[group_node*number_of_group_node_children + or_addend] > 0) {
365
- child_input_sum += child_input[group_node*number_of_group_node_children + or_addend];
366
- }
325
+ }
326
+ } else {
327
+ child_input_sum = 0;
328
+ for (int or_addend = 0; or_addend < number_of_group_node_children; ++or_addend) {
329
+ if (child_input[group_node*number_of_group_node_children + or_addend] > 0) {
330
+ child_input_sum += child_input[group_node*number_of_group_node_children + or_addend];
367
331
  }
368
332
  }
369
- #endif
333
+ }
370
334
 
371
335
  // Skip node if "turned off" (-1)
372
336
  if (group_node_output[group_node] != -1) {
373
337
  int selected_child;
374
- if (child_input_sum > 0) {
338
+ if (group_node_output[group_node] > 0) {
375
339
  int random_child_value = curand(&localState) % child_input_sum;
376
340
  child_input_sum = 0;
377
341
  for (int or_addend = 0; or_addend < number_of_group_node_children; ++or_addend) {
@@ -405,39 +369,15 @@ code_update = """
405
369
 
406
370
  // Add up the votes from the children of each OR node
407
371
  for (int or_alternatives_node = index; or_alternatives_node < CLAUSES*number_of_or_alternatives_nodes; or_alternatives_node += stride) {
408
- #if LOG_SCALE == 1
409
- float or_alternatives_vote_max = NEG_INFINITY;
410
- for (int or_alternative = 0; or_alternative < number_of_or_alternatives; ++or_alternative) {
411
- if (child_input[or_alternatives_node * number_of_or_alternatives + or_alternative] > or_alternatives_vote_max) {
412
- or_alternatives_vote_max = child_input[or_alternatives_node * number_of_or_alternatives + or_alternative];
413
- }
414
- }
415
-
416
- if (or_alternatives_vote_max != NEG_INFINITY) {
417
- // Sum up votes from each or alternative
418
- float or_alternatives_vote_sum = 0;
419
- for (int or_alternative = 0; or_alternative < number_of_or_alternatives; ++or_alternative) {
420
- // Aggregates or alternatives through summation
421
-
422
- or_alternatives_vote_sum += exp2f(child_input[or_alternatives_node * number_of_or_alternatives + or_alternative] - or_alternatives_vote_max);
423
- }
424
-
425
- // Store vote sum as node output
426
- or_alternatives_node_output[or_alternatives_node] = or_alternatives_vote_max + log2f(or_alternatives_vote_sum);
427
- } else {
428
- or_alternatives_node_output[or_alternatives_node] = NEG_INFINITY;
429
- }
430
- #else
431
- // Sum up votes from each or alternative
432
- int or_alternatives_vote_sum = 0;
433
- for (int or_alternative = 0; or_alternative < number_of_or_alternatives; ++or_alternative) {
434
- // Aggregate same input or alternatives through summation
435
- or_alternatives_vote_sum += child_input[or_alternatives_node * number_of_or_alternatives + or_alternative];
436
- }
372
+ // Sum up votes from each or alternative
373
+ int or_alternatives_vote_sum = 0;
374
+ for (int or_alternative = 0; or_alternative < number_of_or_alternatives; ++or_alternative) {
375
+ // Aggregate same input or alternatives through summation
376
+ or_alternatives_vote_sum += child_input[or_alternatives_node * number_of_or_alternatives + or_alternative];
377
+ }
437
378
 
438
- // Store vote sum as node output
439
- or_alternatives_node_output[or_alternatives_node] = or_alternatives_vote_sum;
440
- #endif
379
+ // Store vote sum as node output
380
+ or_alternatives_node_output[or_alternatives_node] = or_alternatives_vote_sum;
441
381
  }
442
382
  }
443
383
 
@@ -458,41 +398,18 @@ code_update = """
458
398
  int stride = blockDim.x * gridDim.x;
459
399
 
460
400
  // Add up the votes from each clause
461
- #if LOG_SCALE == 1
462
- // Add up the votes from each clause
463
- for (int class_id = index; class_id < number_of_outputs; class_id += stride) {
464
- float clause_output_max = NEG_INFINITY;
465
- for (int clause = 0; clause < CLAUSES; ++clause) {
466
- if (clause_output[clause] > clause_output_max) {
467
- clause_output_max = clause_output[clause];
468
- }
469
- }
470
401
 
471
- if (clause_output_max != NEG_INFINITY) {
472
- float weighted_clause_output_sum = 0;
473
- for (int clause = 0; clause < CLAUSES; ++clause) {
474
- weighted_clause_output_sum += clause_weights[class_id*CLAUSES + clause] * exp2f(clause_output[clause] - clause_output_max);
475
- }
476
-
477
- if (log2f(fabs(weighted_clause_output_sum)) + clause_output_max >= log2f(THRESHOLD)) {
478
- float sign = (1 - 2 * (weighted_clause_output_sum < 0));
479
- class_sum[class_id] = sign*THRESHOLD;
480
- } else {
481
- class_sum[class_id] = weighted_clause_output_sum * exp2f(clause_output_max);
482
- }
483
- } else {
484
- class_sum[class_id] = 0;
485
- }
486
- }
487
- #else
488
- for (int clause = index; clause < CLAUSES; clause += stride) {
489
- if (clause_output[clause]) {
490
- for (int class_id = 0; class_id < number_of_outputs; ++class_id) {
402
+ for (int clause = index; clause < CLAUSES; clause += stride) {
403
+ if (clause_output[clause]) {
404
+ for (int class_id = 0; class_id < number_of_outputs; ++class_id) {
405
+ #if BINARY_INFERENCE == 0
491
406
  atomicAdd(&class_sum[class_id], (float) clause_weights[class_id*CLAUSES + clause] * clause_output[clause]);
492
- }
493
- }
407
+ #else
408
+ atomicAdd(&class_sum[class_id], (float) clause_weights[class_id*CLAUSES + clause]);
409
+ #endif
410
+ }
494
411
  }
495
- #endif
412
+ }
496
413
  }
497
414
 
498
415
  __global__ void rescale_final(int number_of_outputs, float *clause_output_max, float *class_sum)
@@ -571,11 +488,7 @@ code_update = """
571
488
  local_class_sum = -THRESHOLD;
572
489
  }
573
490
 
574
- #if LOG_SCALE == 1
575
- update_component_hierarchy(&localState, number_of_outputs, &clause_weights[class_id*CLAUSES + clause], ta_state, component_output[clause_component] != NEG_INFINITY, &Xi[feature_chunk_base], y[example*number_of_outputs + class_id], local_class_sum);
576
- #else
577
- update_component_hierarchy(&localState, number_of_outputs, &clause_weights[class_id*CLAUSES + clause], ta_state, component_output[clause_component] > 0, &Xi[feature_chunk_base], y[example*number_of_outputs + class_id], local_class_sum);
578
- #endif
491
+ update_component_hierarchy(&localState, number_of_outputs, &clause_weights[class_id*CLAUSES + clause], ta_state, component_output[clause_component] > 0, &Xi[feature_chunk_base], y[example*number_of_outputs + class_id], local_class_sum);
579
492
  }
580
493
  }
581
494
 
@@ -600,11 +513,7 @@ code_update = """
600
513
  local_class_sum = -THRESHOLD;
601
514
  }
602
515
 
603
- #if LOG_SCALE == 1
604
- update_clause_weight(&localState, tm_type, number_of_outputs, &clause_weights[class_id*CLAUSES + clause], clause_output[clause] != NEG_INFINITY, y[example*number_of_outputs + class_id], local_class_sum);
605
- #else
606
- update_clause_weight(&localState, tm_type, number_of_outputs, &clause_weights[class_id*CLAUSES + clause], clause_output[clause] > 0, y[example*number_of_outputs + class_id], local_class_sum);
607
- #endif
516
+ update_clause_weight(&localState, tm_type, number_of_outputs, &clause_weights[class_id*CLAUSES + clause], clause_output[clause] > 0, y[example*number_of_outputs + class_id], local_class_sum);
608
517
  }
609
518
  }
610
519
 
@@ -25,7 +25,7 @@ import PyHierarchicalTsetlinMachineCUDA.kernels as kernels
25
25
 
26
26
  import pycuda.curandom as curandom
27
27
  import pycuda.driver as cuda
28
- import pycuda.autoinit
28
+ import pycuda.autoprimaryctx
29
29
  from pycuda.compiler import SourceModule
30
30
  from pycuda import gpuarray
31
31
  from pycuda.gpuarray import GPUArray
@@ -44,13 +44,16 @@ COALESCED_TM = 2
44
44
 
45
45
  class CommonTsetlinMachine():
46
46
 
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):
47
+ def __init__(self, number_of_clauses, T, s, q=1.0, no_clipping=False, constant_update_p=0, binary_inference=False, and_group_normalization=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):
48
48
  self.number_of_clauses = number_of_clauses
49
49
  self.number_of_state_bits = number_of_state_bits
50
- self.T = int(T)
50
+ self.T = float(T)
51
51
  self.s = s
52
52
  self.q = q
53
- self.log_scale = log_scale
53
+ self.no_clipping = no_clipping
54
+ self.constant_update_p = constant_update_p
55
+ self.binary_inference = binary_inference
56
+ self.and_group_normalization = and_group_normalization
54
57
  self.hierarchy_structure = hierarchy_structure
55
58
  self.depth = len(hierarchy_structure)
56
59
 
@@ -127,7 +130,9 @@ class CommonTsetlinMachine():
127
130
  #define S %f
128
131
  #define THRESHOLD %d
129
132
  #define Q %f
130
- #define LOG_SCALE %d
133
+ #define BINARY_INFERENCE %d
134
+ #define CONSTANT_UPDATE_P %d
135
+ #define AND_GROUP_NORMALIZATION %d
131
136
 
132
137
  #define AND_GROUP %d
133
138
  #define AND_ALTERNATIVES %d
@@ -136,8 +141,8 @@ class CommonTsetlinMachine():
136
141
 
137
142
  #define NEGATIVE_CLAUSES %d
138
143
  #define FLIP_POLARITY %d
139
- """ % (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.negative_clauses, self.flip_polarity)
140
-
144
+ """ % (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.binary_inference, self.constant_update_p, self.and_group_normalization, AND_GROUP, AND_ALTERNATIVES, OR_GROUP, OR_ALTERNATIVES, self.negative_clauses, self.flip_polarity)
145
+
141
146
  mod_prepare = SourceModule(parameters + kernels.code_header + kernels.code_prepare, no_extern_c=True)
142
147
  self.prepare_weights = mod_prepare.get_function("prepare_weights")
143
148
  self.prepare_hierarchy = mod_prepare.get_function("prepare_hierarchy")
@@ -329,15 +334,6 @@ class CommonTsetlinMachine():
329
334
  self.clause_output_max[:] = np.finfo(np.float32).min
330
335
  cuda.memcpy_htod(self.clause_output_max_gpu, self.clause_output_max)
331
336
 
332
- # if self.log_scale:
333
- # self.max_clause_output.prepared_call(
334
- # self.grid,
335
- # self.block,
336
- # np.int32(self.number_of_outputs),
337
- # self.hierarchy_votes[self.depth-1],
338
- # self.clause_output_max_gpu
339
- # )
340
-
341
337
  # Adds up the votes from each clause (hierarchy root)
342
338
  self.evaluate_final.prepared_call(
343
339
  self.grid,
@@ -348,15 +344,6 @@ class CommonTsetlinMachine():
348
344
  self.class_sum_gpu
349
345
  )
350
346
 
351
- # if self.log_scale:
352
- # self.rescale_final.prepared_call(
353
- # self.grid,
354
- # self.block,
355
- # np.int32(self.number_of_outputs),
356
- # self.clause_output_max_gpu,
357
- # self.class_sum_gpu
358
- # )
359
-
360
347
  def _fit(self, X, encoded_Y):
361
348
  if self.number_of_features_hierarchy != X.shape[1]:
362
349
  print("The number of features spanned by hierarchy does not align with the input data.")
@@ -445,15 +432,18 @@ class CommonTsetlinMachine():
445
432
  encoded_X_hierarchy_test_gpu = cuda.mem_alloc(int(number_of_examples * self.number_of_literal_chunks * 4))
446
433
  self.encode_X(X, encoded_X_hierarchy_test_gpu)
447
434
 
448
- class_sum = np.ascontiguousarray(np.zeros((self.number_of_outputs, number_of_examples))).astype(np.int32)
435
+ class_sum = np.ascontiguousarray(np.zeros((self.number_of_outputs, number_of_examples))).astype(np.float32)
449
436
 
450
437
  for e in range(number_of_examples):
451
438
  self.evaluate_hierarchy(encoded_X_hierarchy_test_gpu, e)
452
439
 
453
440
  cuda.memcpy_dtoh(self.class_sum, self.class_sum_gpu)
454
- class_sum[:, e] = self.class_sum.astype(np.int32)
441
+ class_sum[:, e] = self.class_sum
455
442
 
456
- class_sum = np.clip(class_sum.reshape((self.number_of_outputs, number_of_examples)), -self.T, self.T)
443
+ if self.no_clipping:
444
+ class_sum = class_sum.reshape((self.number_of_outputs, number_of_examples))
445
+ else:
446
+ class_sum = np.clip(class_sum.reshape((self.number_of_outputs, number_of_examples)), -self.T, self.T)
457
447
 
458
448
  return class_sum
459
449
 
@@ -500,7 +490,7 @@ class CommonTsetlinMachine():
500
490
  continue
501
491
 
502
492
  n_children = self.hierarchy_structure[level][1]
503
- is_alt = (self.hierarchy_structure[level][0] == OR_ALTERNATIVES)
493
+ is_alt = self.hierarchy_structure[level][0] in (OR_ALTERNATIVES, AND_ALTERNATIVES)
504
494
  for child_pos in range(n_children):
505
495
  child_id = node_id * n_children + child_pos
506
496
  if is_alt:
@@ -599,12 +589,12 @@ class CommonTsetlinMachine():
599
589
  if self.ta_action(i, j, k):
600
590
  if (not self.append_negated) or k < self.number_of_literals_per_leaf // 2:
601
591
  if print_ta_state:
602
- l.append("x%d(%d)" % (feature_base + k, self.ta_state(i, j, k)))
592
+ l.append("x%d[%d]" % (feature_base + k, self.ta_state(i, j, k)))
603
593
  else:
604
594
  l.append("x%d" % (feature_base + k,))
605
595
  else:
606
596
  if print_ta_state:
607
- l.append("¬x%d(%d)" % (feature_base + k - self.number_of_literals_per_leaf // 2, self.ta_state(i, j, k)))
597
+ l.append("¬x%d[%d]" % (feature_base + k - self.number_of_literals_per_leaf // 2, self.ta_state(i, j, k)))
608
598
  else:
609
599
  l.append("¬x%d" % (feature_base + k - self.number_of_literals_per_leaf // 2,))
610
600
 
@@ -658,9 +648,9 @@ class CommonTsetlinMachine():
658
648
 
659
649
 
660
650
  class MultiOutputTsetlinMachine(CommonTsetlinMachine):
661
- def __init__(self, number_of_clauses, T, s, q=1.0, log_scale=False, boost_true_positive_feedback=1, number_of_state_bits=8, append_negated=True, grid=(16*13,1,1), block=(128,1,1), seed=None):
651
+ def __init__(self, number_of_clauses, T, s, q=1.0, no_clipping=False, constant_update_p=0, binary_inference=False, and_group_normalization=False, boost_true_positive_feedback=1, number_of_state_bits=8, append_negated=True, grid=(16*13,1,1), block=(128,1,1), seed=None):
662
652
  self.negative_clauses = 1
663
- 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)
653
+ super().__init__(number_of_clauses, T, s, q=q, no_clipping=no_clipping, constant_update_p=constant_update_p, binary_inference=binary_inference, and_group_normalization=and_group_normalization, 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)
664
654
 
665
655
  def fit(self, X, Y):
666
656
  X = X.reshape(X.shape[0], X.shape[1], 1)
@@ -684,12 +674,12 @@ class MultiOutputTsetlinMachine(CommonTsetlinMachine):
684
674
  return (self.score(X) >= 0).astype(np.uint32).transpose()
685
675
 
686
676
  class MultiClassCoalescedTsetlinMachine(CommonTsetlinMachine):
687
- def __init__(self, number_of_clauses, T, s, q=1.0, log_scale=False, 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):
677
+ def __init__(self, number_of_clauses, T, s, q=1.0, no_clipping=False, constant_update_p=0, binary_inference=False, and_group_normalization=False, 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):
688
678
  self.negative_clauses = 1
689
679
  self.tm_type = COALESCED_TM
690
680
  self.flip_polarity = 1
691
681
 
692
- 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)
682
+ super().__init__(number_of_clauses, T, s, q=q, no_clipping=no_clipping, constant_update_p=constant_update_p, binary_inference=binary_inference, and_group_normalization=and_group_normalization, 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)
693
683
 
694
684
  def fit(self, X, Y):
695
685
  X = X.reshape(X.shape[0], X.shape[1], 1)
@@ -716,12 +706,15 @@ class MultiClassCoalescedTsetlinMachine(CommonTsetlinMachine):
716
706
  return np.argmax(self.score(X), axis=0)
717
707
 
718
708
  class MultiClassTsetlinMachine:
719
- def __init__(self, number_of_clauses, T, s, q=1.0, log_scale=False, weighted_clauses=False, 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):
709
+ def __init__(self, number_of_clauses, T, s, q=1.0, no_clipping=False, constant_update_p=0, binary_inference=False, and_group_normalization=False, weighted_clauses=False, 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):
720
710
  self.number_of_clauses = number_of_clauses
721
711
  self.T = T
722
712
  self.s = s
723
713
  self.q = q
724
- self.log_scale = log_scale
714
+ self.no_clipping = no_clipping
715
+ self.constant_update_p = constant_update_p
716
+ self.binary_inference = binary_inference
717
+ self.and_group_normalization = and_group_normalization
725
718
  self.weighted_clauses = weighted_clauses
726
719
  self.hierarchy_structure = hierarchy_structure
727
720
  self.boost_true_positive_feedback = boost_true_positive_feedback
@@ -739,7 +732,7 @@ class MultiClassTsetlinMachine:
739
732
  if not self.configured:
740
733
  self.tms = []
741
734
  for i in range(self.number_of_outputs):
742
- self.tms.append(TsetlinMachine(self.number_of_clauses, self.T, self.s, q=self.q, log_scale=self.log_scale, weighted_clauses=self.weighted_clauses, hierarchy_structure=self.hierarchy_structure, 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))
735
+ self.tms.append(TsetlinMachine(self.number_of_clauses, self.T, self.s, q=self.q, no_clipping=self.no_clipping, constant_update_p=self.constant_update_p, binary_inference=self.binary_inference, and_group_normalization=self.and_group_normalization, weighted_clauses=self.weighted_clauses, hierarchy_structure=self.hierarchy_structure, 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))
743
736
 
744
737
  self.configured = True
745
738
 
@@ -760,7 +753,7 @@ class MultiClassTsetlinMachine:
760
753
  return
761
754
 
762
755
  def score(self, X):
763
- class_sums = np.empty((self.number_of_outputs, X.shape[0]), dtype=np.int32)
756
+ class_sums = np.empty((self.number_of_outputs, X.shape[0]), dtype=np.float32)
764
757
  for i in range(self.number_of_outputs):
765
758
  class_sums[i,:] = self.tms[i].score(X)
766
759
 
@@ -790,7 +783,7 @@ class MultiClassTsetlinMachine:
790
783
 
791
784
  def print_hierarchy(self, print_ta_state=False):
792
785
  for i in range(self.number_of_outputs):
793
- print("CLASS %d\n", i)
786
+ print("\nCLASS %d\n" % (i,))
794
787
  self.tms[i].print_hierarchy(print_ta_state = print_ta_state)
795
788
 
796
789
  def load(self, state_dict: dict):
@@ -805,7 +798,7 @@ class MultiClassTsetlinMachine:
805
798
  self.configured = True
806
799
 
807
800
  class TsetlinMachine(CommonTsetlinMachine):
808
- def __init__(self, number_of_clauses, T, s, q=1.0, log_scale=False, weighted_clauses=False, 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):
801
+ def __init__(self, number_of_clauses, T, s, q=1.0, no_clipping=False, constant_update_p=0, binary_inference=False, and_group_normalization=False, weighted_clauses=False, 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):
809
802
  self.negative_clauses = 1
810
803
  self.flip_polarity = 0
811
804
 
@@ -814,7 +807,7 @@ class TsetlinMachine(CommonTsetlinMachine):
814
807
  else:
815
808
  self.tm_type = VANILLA_TM
816
809
 
817
- 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)
810
+ super().__init__(number_of_clauses, T, s, q=q, no_clipping=no_clipping, constant_update_p=constant_update_p, binary_inference=binary_inference, and_group_normalization=and_group_normalization, 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)
818
811
 
819
812
  def fit(self, X, Y):
820
813
  X = X.reshape(X.shape[0], X.shape[1], 1)
@@ -838,11 +831,11 @@ class TsetlinMachine(CommonTsetlinMachine):
838
831
  return (self.score(X) >= 0).astype(np.int32)
839
832
 
840
833
  class RegressionTsetlinMachine(CommonTsetlinMachine):
841
- def __init__(self, number_of_clauses, T, s, log_scale=False, 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):
834
+ def __init__(self, number_of_clauses, T, s, and_group_normalization=False, 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):
842
835
  self.negative_clauses = 0
843
836
  self.flip_polarity = 0
844
837
 
845
- 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)
838
+ super().__init__(number_of_clauses, T, s, and_group_normalization=and_group_normalization, 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)
846
839
 
847
840
  def fit(self, X, Y):
848
841
  X = X.reshape(X.shape[0], X.shape[1], 1)
@@ -3,17 +3,31 @@ from collections import deque
3
3
  import networkx as nx
4
4
  import numpy as np
5
5
 
6
- from .tm import OR_ALTERNATIVES, CommonTsetlinMachine
6
+ from .tm import AND_ALTERNATIVES, AND_GROUP, OR_ALTERNATIVES, OR_GROUP, CommonTsetlinMachine
7
7
 
8
+ DEFAULT_NODE_TYPE_LABELS: dict[int, str] = {
9
+ AND_GROUP: ' ∧* ',
10
+ AND_ALTERNATIVES: ' ∧ ',
11
+ OR_GROUP: ' ∨* ',
12
+ OR_ALTERNATIVES: ' ∨ ',
13
+ }
8
14
 
9
- def make_hierarchy_graph(G: nx.Graph, hier: list[tuple[int, int]]):
15
+
16
+ def make_hierarchy_graph(
17
+ G: nx.Graph,
18
+ hier: list[tuple[int, int]],
19
+ node_type_labels: dict[int, str] | None = None,
20
+ ):
10
21
  """Create Graph from the hierarchy."""
22
+ node_type_labels = {**DEFAULT_NODE_TYPE_LABELS, **(node_type_labels or {})}
23
+
11
24
  # BFS
12
25
  q = deque()
13
26
  q.append((len(hier), 0)) # (level, idx)
14
27
  while q:
15
28
  level, idx = q.popleft()
16
- G.add_node((level, idx), label=hier[level - 1][0], op=hier[level - 1][0])
29
+ op = hier[level - 1][0]
30
+ G.add_node((level, idx), label=node_type_labels.get(op, op), op=op)
17
31
  if level == 1:
18
32
  continue
19
33
  branching = hier[level - 1][1]
@@ -29,6 +43,7 @@ def clause_to_nx(
29
43
  negation_prefix: str = '¬',
30
44
  clause_literals: np.ndarray | None = None,
31
45
  ta_to_fid_mapping=None,
46
+ node_type_labels: dict[int, str] | None = None,
32
47
  ):
33
48
  """
34
49
  Create a networkx Graph for a single clause.
@@ -39,6 +54,7 @@ def clause_to_nx(
39
54
  `negation_prefix`: Prefix for negated features (default: '¬').
40
55
  `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
56
  `ta_to_fid_mapping`: Optional mapping from (component_id, literal_id) to feature_id. If None, it will be obtained from the model.
57
+ `node_type_labels`: Optional dict overriding the display symbol for one or more hierarchy node types (`AND_GROUP`/`AND_ALTERNATIVES`/`OR_GROUP`/`OR_ALTERNATIVES`). Merged over `DEFAULT_NODE_TYPE_LABELS`; unset keys fall back to the default.
42
58
  """
43
59
 
44
60
  assert tm.hierarchy_structure is not None, (
@@ -62,7 +78,7 @@ def clause_to_nx(
62
78
  lits_per_comp = tm.number_of_literals_per_leaf
63
79
 
64
80
  G = nx.Graph()
65
- make_hierarchy_graph(G, tm.hierarchy_structure)
81
+ make_hierarchy_graph(G, tm.hierarchy_structure, node_type_labels)
66
82
 
67
83
  feat_per_comp = lits_per_comp // 2 if tm.append_negated else lits_per_comp
68
84
 
@@ -95,6 +111,7 @@ def clause_bank_to_nx(
95
111
  tm: CommonTsetlinMachine,
96
112
  feature_names: list[str] | None = None,
97
113
  negation_prefix: str = '¬',
114
+ node_type_labels: dict[int, str] | None = None,
98
115
  ):
99
116
  """
100
117
  Create a networkx Graph for the entire clause bank.
@@ -102,15 +119,18 @@ def clause_bank_to_nx(
102
119
  `tm`: The model.
103
120
  `feature_names`: Optional list of feature names for labeling. If None, defaults to 'x0', 'x1', ...
104
121
  `negation_prefix`: Prefix for negated features (default: '¬').
122
+ `node_type_labels`: Optional dict overriding the display symbol for one or more hierarchy node types. Merged over `DEFAULT_NODE_TYPE_LABELS`; unset keys fall back to the default.
105
123
  """
124
+ node_type_labels = {**DEFAULT_NODE_TYPE_LABELS, **(node_type_labels or {})}
125
+
106
126
  literals = tm.get_literals()
107
127
  ta_to_fid = tm.map_ta_id_to_feature_id()
108
128
  G = nx.Graph()
109
129
  cb_root = 'CB_ROOT'
110
- G.add_node(cb_root, label=OR_ALTERNATIVES, op=OR_ALTERNATIVES)
130
+ G.add_node(cb_root, label=node_type_labels.get(OR_ALTERNATIVES, OR_ALTERNATIVES), op=OR_ALTERNATIVES)
111
131
  for ci in range(tm.number_of_clauses):
112
132
  clause_G = clause_to_nx(
113
- tm, ci, feature_names, negation_prefix, literals[ci], ta_to_fid
133
+ tm, ci, feature_names, negation_prefix, literals[ci], ta_to_fid, node_type_labels
114
134
  )
115
135
  mapping = {node: (ci, node) for node in clause_G.nodes}
116
136
  G = nx.compose(G, nx.relabel_nodes(clause_G, mapping))
@@ -124,6 +144,7 @@ def active_path_graph(
124
144
  X: np.ndarray,
125
145
  feature_names: list[str] | None = None,
126
146
  negation_prefix: str = '¬',
147
+ node_type_labels: dict[int, str] | None = None,
127
148
  ):
128
149
  """
129
150
  Show the activated path, and vote calculation for a single sample `X`.
@@ -132,6 +153,7 @@ def active_path_graph(
132
153
  `X`: A single sample with shape (n_features,).
133
154
  `feature_names`: Optional list of feature names for labeling. If None, defaults to 'x0', 'x1', ...
134
155
  `negation_prefix`: Prefix for negated features (default: '¬').
156
+ `node_type_labels`: Optional dict overriding the display symbol for one or more hierarchy node types. Merged over `DEFAULT_NODE_TYPE_LABELS`; unset keys fall back to the default.
135
157
  Returns:
136
158
  A list of networkx Graphs for each clause, with 'active' and 'vote' attributes on nodes and edges, and class sums for each class.
137
159
  """
@@ -139,7 +161,7 @@ def active_path_graph(
139
161
 
140
162
  hierarchy_votes, class_sums = tm.calc_hierarchy_votes(X.reshape(1, -1))
141
163
  hierarchy_votes = hierarchy_votes[0]
142
- class_sums = class_sums[0]
164
+ class_sums = class_sums[:, 0]
143
165
 
144
166
  literals = tm.get_literals()
145
167
  ta_to_fid = tm.map_ta_id_to_feature_id()
@@ -147,7 +169,7 @@ def active_path_graph(
147
169
  feat_per_comp = lits_per_comp // 2 if tm.append_negated else lits_per_comp
148
170
 
149
171
  G = [
150
- clause_to_nx(tm, ci, feature_names, negation_prefix, literals[ci], ta_to_fid)
172
+ clause_to_nx(tm, ci, feature_names, negation_prefix, literals[ci], ta_to_fid, node_type_labels)
151
173
  for ci in range(tm.number_of_clauses)
152
174
  ]
153
175
  for ci in range(tm.number_of_clauses):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PyHierarchicalTsetlinMachineCUDA
3
- Version: 0.3.1
3
+ Version: 0.3.2
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.8.tar.gz
14
+ pip install dist/PyHierarchicalTsetlinMachineCUDA-0.3.1.tar.gz
15
15
  ```
16
16
 
17
17
  ## Examples
@@ -79,7 +79,7 @@ CLAUSE 1: (((((x0 ∧ x1) ∨ (x0 ∧ x1) ∨ (¬x0 ∧ ¬x1)) ∧ ((x3 ∧ x4)
79
79
  ### Hex Demo
80
80
 
81
81
  <p align="center">
82
- <img width="60%" src="https://github.com/cair/PyHierarchicalTsetlinMachineCUDA/blob/hex_description/figures/Hex-board-11x11.png">
82
+ <img width="60%" src="https://github.com/cair/PyHierarchicalTsetlinMachineCUDA/blob/main/figures/Hex-board-11x11.png">
83
83
  </p>
84
84
  <p align="center">
85
85
  Source: https://en.wikipedia.org/wiki/Hex_(board_game)
@@ -2,7 +2,7 @@ from setuptools import *
2
2
 
3
3
  setup(
4
4
  name='PyHierarchicalTsetlinMachineCUDA',
5
- version='0.3.1',
5
+ version='0.3.2',
6
6
  author='Ole-Christoffer Granmo',
7
7
  author_email='ole.granmo@uia.no',
8
8
  url='https://github.com/cair/PyHierarchicalTsetlinMachineCUDA/',