PyHierarchicalTsetlinMachineCUDA 0.3.1__tar.gz → 0.3.3__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.3}/PKG-INFO +1 -1
  2. {PyHierarchicalTsetlinMachineCUDA-0.3.1 → PyHierarchicalTsetlinMachineCUDA-0.3.3}/PyHierarchicalTsetlinMachineCUDA/kernels.py +81 -154
  3. {PyHierarchicalTsetlinMachineCUDA-0.3.1 → PyHierarchicalTsetlinMachineCUDA-0.3.3}/PyHierarchicalTsetlinMachineCUDA/tm.py +60 -44
  4. {PyHierarchicalTsetlinMachineCUDA-0.3.1 → PyHierarchicalTsetlinMachineCUDA-0.3.3}/PyHierarchicalTsetlinMachineCUDA/utils.py +30 -8
  5. {PyHierarchicalTsetlinMachineCUDA-0.3.1 → PyHierarchicalTsetlinMachineCUDA-0.3.3}/PyHierarchicalTsetlinMachineCUDA.egg-info/PKG-INFO +1 -1
  6. {PyHierarchicalTsetlinMachineCUDA-0.3.1 → PyHierarchicalTsetlinMachineCUDA-0.3.3}/README.md +2 -2
  7. {PyHierarchicalTsetlinMachineCUDA-0.3.1 → PyHierarchicalTsetlinMachineCUDA-0.3.3}/setup.py +1 -1
  8. {PyHierarchicalTsetlinMachineCUDA-0.3.1 → PyHierarchicalTsetlinMachineCUDA-0.3.3}/LICENSE +0 -0
  9. {PyHierarchicalTsetlinMachineCUDA-0.3.1 → PyHierarchicalTsetlinMachineCUDA-0.3.3}/PyHierarchicalTsetlinMachineCUDA/__init__.py +0 -0
  10. {PyHierarchicalTsetlinMachineCUDA-0.3.1 → PyHierarchicalTsetlinMachineCUDA-0.3.3}/PyHierarchicalTsetlinMachineCUDA/tools.py +0 -0
  11. {PyHierarchicalTsetlinMachineCUDA-0.3.1 → PyHierarchicalTsetlinMachineCUDA-0.3.3}/PyHierarchicalTsetlinMachineCUDA.egg-info/SOURCES.txt +0 -0
  12. {PyHierarchicalTsetlinMachineCUDA-0.3.1 → PyHierarchicalTsetlinMachineCUDA-0.3.3}/PyHierarchicalTsetlinMachineCUDA.egg-info/dependency_links.txt +0 -0
  13. {PyHierarchicalTsetlinMachineCUDA-0.3.1 → PyHierarchicalTsetlinMachineCUDA-0.3.3}/PyHierarchicalTsetlinMachineCUDA.egg-info/requires.txt +0 -0
  14. {PyHierarchicalTsetlinMachineCUDA-0.3.1 → PyHierarchicalTsetlinMachineCUDA-0.3.3}/PyHierarchicalTsetlinMachineCUDA.egg-info/top_level.txt +0 -0
  15. {PyHierarchicalTsetlinMachineCUDA-0.3.1 → PyHierarchicalTsetlinMachineCUDA-0.3.3}/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.3
4
4
  Summary: Hierarchical Tsetlin Machine Architecture.
5
5
  Home-page: https://github.com/cair/PyHierarchicalTsetlinMachineCUDA/
6
6
  Author: Ole-Christoffer Granmo
@@ -41,6 +41,21 @@ code_header = """
41
41
  code_update = """
42
42
  extern "C"
43
43
  {
44
+
45
+ // Counts number of include actions for a given clause component
46
+ __device__ inline int number_of_include_actions(unsigned int *ta_state)
47
+ {
48
+ int number_of_include_actions = 0;
49
+ for (int k = 0; k < TA_CHUNKS_PER_LEAF-1; ++k) {
50
+ unsigned int ta_pos = k*STATE_BITS + STATE_BITS-1;
51
+ number_of_include_actions += __popc(ta_state[ta_pos]);
52
+ }
53
+ unsigned int ta_pos = (TA_CHUNKS_PER_LEAF-1)*STATE_BITS + STATE_BITS-1;
54
+ number_of_include_actions += __popc(ta_state[ta_pos] & FILTER_HIERARCHICAL);
55
+
56
+ return(number_of_include_actions);
57
+ }
58
+
44
59
  // Increment the states of each of those 32 Tsetlin Automata flagged in the active bit vector.
45
60
  __device__ inline void inc(unsigned int *ta_state, int chunk, unsigned int active)
46
61
  {
@@ -94,8 +109,14 @@ code_update = """
94
109
 
95
110
  int sign = (*clause_weight >= 0) - (*clause_weight < 0);
96
111
 
97
- float absolute_prediction_error = fabsf(y - class_sum);
98
- if (curand_uniform(localState) <= 1.0*absolute_prediction_error/(2*THRESHOLD)) {
112
+ float update_probability;
113
+ #if CONSTANT_UPDATE_P == 1
114
+ update_probability = 1.0;
115
+ #else
116
+ update_probability = fabsf(y - class_sum)/(2*THRESHOLD);
117
+ #endif
118
+
119
+ if (curand_uniform(localState) <= update_probability) {
99
120
  if (target*sign > 0) {
100
121
  if (clause_output && abs(*clause_weight) < INT_MAX) {
101
122
  (*clause_weight) += sign;
@@ -124,10 +145,19 @@ code_update = """
124
145
 
125
146
  int sign = (*clause_weight >= 0) - (*clause_weight < 0);
126
147
 
127
- float absolute_prediction_error = fabsf(y - class_sum);
128
- if (curand_uniform(localState) <= 1.0*absolute_prediction_error/(2*THRESHOLD)) {
148
+ float update_probability;
149
+ #if CONSTANT_UPDATE_P == 1
150
+ update_probability = 1.0;
151
+ #else
152
+ update_probability = fabsf(y - class_sum)/(2*THRESHOLD);
153
+ #endif
154
+
155
+ if (curand_uniform(localState) <= update_probability) {
129
156
  if (target*sign > 0) {
130
157
  // Type I Feedback
158
+
159
+ int included_literals = number_of_include_actions(ta_state);
160
+
131
161
  for (int ta_chunk = 0; ta_chunk < TA_CHUNKS_PER_LEAF; ++ta_chunk) {
132
162
  // Generate random bit values
133
163
  unsigned int la_feedback = 0;
@@ -137,7 +167,7 @@ code_update = """
137
167
  }
138
168
  }
139
169
 
140
- if (component_output) {
170
+ if (component_output && included_literals <= LITERAL_BUDGET) {
141
171
  #if BOOST_TRUE_POSITIVE_FEEDBACK == 1
142
172
  inc(ta_state, ta_chunk, X[ta_chunk]);
143
173
  #else
@@ -199,31 +229,19 @@ code_update = """
199
229
 
200
230
  // Evaluate clause component
201
231
 
202
- #if LOG_SCALE == 1
203
- float component_output = 0;
204
- #else
205
- float component_output = 1;
206
- #endif
232
+ float component_output = 1;
207
233
 
208
234
  for (int ta_chunk = 0; ta_chunk < TA_CHUNKS_PER_LEAF-1; ++ta_chunk) {
209
235
  // Compare the TA state of the component (leaf) against the corresponding part of the feature vector
210
236
  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
237
+ component_output = 0;
216
238
 
217
239
  break;
218
240
  }
219
241
  }
220
242
 
221
243
  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
244
+ component_output = 0;
227
245
  }
228
246
 
229
247
  global_component_output[clause_component] = component_output;
@@ -262,17 +280,12 @@ code_update = """
262
280
  for (int and_group_node = index; and_group_node < CLAUSES*number_of_and_group_nodes; and_group_node += stride) {
263
281
  // Multiply and factors
264
282
 
265
- #if LOG_SCALE == 1
266
- float and_group_vote_product = 0;
267
- #else
268
- float and_group_vote_product = 1;
269
- #endif
270
-
283
+ float and_group_vote_product = 1;
271
284
  for (int and_factor = 0; and_factor < number_of_and_group_factors; ++and_factor) {
272
285
  // Aggregate votes from each child node through multiplication
273
286
 
274
- #if LOG_SCALE == 1
275
- and_group_vote_product += child_input[and_group_node*number_of_and_group_factors + and_factor];
287
+ #if AND_GROUP_NORMALIZATION == 1
288
+ 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
289
  #else
277
290
  and_group_vote_product *= child_input[and_group_node*number_of_and_group_factors + and_factor];
278
291
  #endif
@@ -289,24 +302,14 @@ code_update = """
289
302
  int stride = blockDim.x * gridDim.x;
290
303
 
291
304
  // 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
- }
305
+ for (int group_node = index; group_node < CLAUSES*number_of_group_nodes; group_node += stride) {
306
+ if (group_node_output[group_node] == 0) {
307
+ for (int and_factor = 0; and_factor < number_of_group_node_children; ++and_factor) {
308
+ if (child_input[group_node*number_of_group_node_children + and_factor] > 0) {
309
+ child_input[group_node*number_of_group_node_children + and_factor] = 0;
307
310
  }
308
311
  }
309
- #endif
312
+ }
310
313
 
311
314
  if (group_node_output[group_node] == -1) {
312
315
  for (int and_factor = 0; and_factor < number_of_group_node_children; ++and_factor) {
@@ -328,50 +331,29 @@ code_update = """
328
331
 
329
332
  // If a group node is false, all children are made false.
330
333
  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
- }
334
+ if (group_node_output[group_node] == -1) {
335
+ for (int or_addend = 0; or_addend < number_of_group_node_children; ++or_addend) {
336
+ child_input[group_node*number_of_group_node_children + or_addend] = -1;
349
337
  }
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
- }
338
+ } else if (group_node_output[group_node] == 0) {
339
+ for (int or_addend = 0; or_addend < number_of_group_node_children; ++or_addend) {
340
+ if (child_input[group_node*number_of_group_node_children + or_addend] > 0) {
341
+ child_input[group_node*number_of_group_node_children + or_addend] = 0;
360
342
  }
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
- }
343
+ }
344
+ } else {
345
+ child_input_sum = 0;
346
+ for (int or_addend = 0; or_addend < number_of_group_node_children; ++or_addend) {
347
+ if (child_input[group_node*number_of_group_node_children + or_addend] > 0) {
348
+ child_input_sum += child_input[group_node*number_of_group_node_children + or_addend];
367
349
  }
368
350
  }
369
- #endif
351
+ }
370
352
 
371
353
  // Skip node if "turned off" (-1)
372
354
  if (group_node_output[group_node] != -1) {
373
355
  int selected_child;
374
- if (child_input_sum > 0) {
356
+ if (group_node_output[group_node] > 0) {
375
357
  int random_child_value = curand(&localState) % child_input_sum;
376
358
  child_input_sum = 0;
377
359
  for (int or_addend = 0; or_addend < number_of_group_node_children; ++or_addend) {
@@ -405,39 +387,15 @@ code_update = """
405
387
 
406
388
  // Add up the votes from the children of each OR node
407
389
  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
- }
390
+ // Sum up votes from each or alternative
391
+ int or_alternatives_vote_sum = 0;
392
+ for (int or_alternative = 0; or_alternative < number_of_or_alternatives; ++or_alternative) {
393
+ // Aggregate same input or alternatives through summation
394
+ or_alternatives_vote_sum += child_input[or_alternatives_node * number_of_or_alternatives + or_alternative];
395
+ }
437
396
 
438
- // Store vote sum as node output
439
- or_alternatives_node_output[or_alternatives_node] = or_alternatives_vote_sum;
440
- #endif
397
+ // Store vote sum as node output
398
+ or_alternatives_node_output[or_alternatives_node] = or_alternatives_vote_sum;
441
399
  }
442
400
  }
443
401
 
@@ -458,41 +416,18 @@ code_update = """
458
416
  int stride = blockDim.x * gridDim.x;
459
417
 
460
418
  // 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
419
 
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) {
420
+ for (int clause = index; clause < CLAUSES; clause += stride) {
421
+ if (clause_output[clause]) {
422
+ for (int class_id = 0; class_id < number_of_outputs; ++class_id) {
423
+ #if BINARY_INFERENCE == 0
491
424
  atomicAdd(&class_sum[class_id], (float) clause_weights[class_id*CLAUSES + clause] * clause_output[clause]);
492
- }
493
- }
425
+ #else
426
+ atomicAdd(&class_sum[class_id], (float) clause_weights[class_id*CLAUSES + clause]);
427
+ #endif
428
+ }
494
429
  }
495
- #endif
430
+ }
496
431
  }
497
432
 
498
433
  __global__ void rescale_final(int number_of_outputs, float *clause_output_max, float *class_sum)
@@ -571,11 +506,7 @@ code_update = """
571
506
  local_class_sum = -THRESHOLD;
572
507
  }
573
508
 
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
509
+ 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
510
  }
580
511
  }
581
512
 
@@ -600,11 +531,7 @@ code_update = """
600
531
  local_class_sum = -THRESHOLD;
601
532
  }
602
533
 
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
534
+ 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
535
  }
609
536
  }
610
537
 
@@ -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,17 @@ 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, literal_budget=None, 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.literal_budget = literal_budget
54
+ self.no_clipping = no_clipping
55
+ self.constant_update_p = constant_update_p
56
+ self.binary_inference = binary_inference
57
+ self.and_group_normalization = and_group_normalization
54
58
  self.hierarchy_structure = hierarchy_structure
55
59
  self.depth = len(hierarchy_structure)
56
60
 
@@ -89,6 +93,9 @@ class CommonTsetlinMachine():
89
93
  self.number_of_literals_per_leaf = self.number_of_features_per_leaf
90
94
  self.number_of_literal_chunks_per_leaf = int((self.number_of_literals_per_leaf - 1) / 32 + 1)
91
95
 
96
+ if self.literal_budget == None:
97
+ self.literal_budget = self.number_of_literals_per_leaf
98
+
92
99
  # Calculates the number of literal chunks for the full hierarchy
93
100
  self.hierarchy_size[0] = self.number_of_literal_chunks_per_leaf * self.hierarchy_size[1]
94
101
 
@@ -127,7 +134,10 @@ class CommonTsetlinMachine():
127
134
  #define S %f
128
135
  #define THRESHOLD %d
129
136
  #define Q %f
130
- #define LOG_SCALE %d
137
+ #define LITERAL_BUDGET %d
138
+ #define BINARY_INFERENCE %d
139
+ #define CONSTANT_UPDATE_P %d
140
+ #define AND_GROUP_NORMALIZATION %d
131
141
 
132
142
  #define AND_GROUP %d
133
143
  #define AND_ALTERNATIVES %d
@@ -136,8 +146,8 @@ class CommonTsetlinMachine():
136
146
 
137
147
  #define NEGATIVE_CLAUSES %d
138
148
  #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
-
149
+ """ % (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.literal_budget, 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)
150
+
141
151
  mod_prepare = SourceModule(parameters + kernels.code_header + kernels.code_prepare, no_extern_c=True)
142
152
  self.prepare_weights = mod_prepare.get_function("prepare_weights")
143
153
  self.prepare_hierarchy = mod_prepare.get_function("prepare_hierarchy")
@@ -329,15 +339,6 @@ class CommonTsetlinMachine():
329
339
  self.clause_output_max[:] = np.finfo(np.float32).min
330
340
  cuda.memcpy_htod(self.clause_output_max_gpu, self.clause_output_max)
331
341
 
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
342
  # Adds up the votes from each clause (hierarchy root)
342
343
  self.evaluate_final.prepared_call(
343
344
  self.grid,
@@ -348,15 +349,6 @@ class CommonTsetlinMachine():
348
349
  self.class_sum_gpu
349
350
  )
350
351
 
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
352
  def _fit(self, X, encoded_Y):
361
353
  if self.number_of_features_hierarchy != X.shape[1]:
362
354
  print("The number of features spanned by hierarchy does not align with the input data.")
@@ -445,15 +437,18 @@ class CommonTsetlinMachine():
445
437
  encoded_X_hierarchy_test_gpu = cuda.mem_alloc(int(number_of_examples * self.number_of_literal_chunks * 4))
446
438
  self.encode_X(X, encoded_X_hierarchy_test_gpu)
447
439
 
448
- class_sum = np.ascontiguousarray(np.zeros((self.number_of_outputs, number_of_examples))).astype(np.int32)
440
+ class_sum = np.ascontiguousarray(np.zeros((self.number_of_outputs, number_of_examples))).astype(np.float32)
449
441
 
450
442
  for e in range(number_of_examples):
451
443
  self.evaluate_hierarchy(encoded_X_hierarchy_test_gpu, e)
452
444
 
453
445
  cuda.memcpy_dtoh(self.class_sum, self.class_sum_gpu)
454
- class_sum[:, e] = self.class_sum.astype(np.int32)
446
+ class_sum[:, e] = self.class_sum
455
447
 
456
- class_sum = np.clip(class_sum.reshape((self.number_of_outputs, number_of_examples)), -self.T, self.T)
448
+ if self.no_clipping:
449
+ class_sum = class_sum.reshape((self.number_of_outputs, number_of_examples))
450
+ else:
451
+ class_sum = np.clip(class_sum.reshape((self.number_of_outputs, number_of_examples)), -self.T, self.T)
457
452
 
458
453
  return class_sum
459
454
 
@@ -500,7 +495,7 @@ class CommonTsetlinMachine():
500
495
  continue
501
496
 
502
497
  n_children = self.hierarchy_structure[level][1]
503
- is_alt = (self.hierarchy_structure[level][0] == OR_ALTERNATIVES)
498
+ is_alt = self.hierarchy_structure[level][0] in (OR_ALTERNATIVES, AND_ALTERNATIVES)
504
499
  for child_pos in range(n_children):
505
500
  child_id = node_id * n_children + child_pos
506
501
  if is_alt:
@@ -599,12 +594,12 @@ class CommonTsetlinMachine():
599
594
  if self.ta_action(i, j, k):
600
595
  if (not self.append_negated) or k < self.number_of_literals_per_leaf // 2:
601
596
  if print_ta_state:
602
- l.append("x%d(%d)" % (feature_base + k, self.ta_state(i, j, k)))
597
+ l.append("x%d[%d]" % (feature_base + k, self.ta_state(i, j, k)))
603
598
  else:
604
599
  l.append("x%d" % (feature_base + k,))
605
600
  else:
606
601
  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)))
602
+ l.append("¬x%d[%d]" % (feature_base + k - self.number_of_literals_per_leaf // 2, self.ta_state(i, j, k)))
608
603
  else:
609
604
  l.append("¬x%d" % (feature_base + k - self.number_of_literals_per_leaf // 2,))
610
605
 
@@ -658,9 +653,9 @@ class CommonTsetlinMachine():
658
653
 
659
654
 
660
655
  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):
656
+ def __init__(self, number_of_clauses, T, s, q=1.0, literal_budget=None, 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
657
  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)
658
+ super().__init__(number_of_clauses, T, s, q=q, literal_budget=literal_budget, 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
659
 
665
660
  def fit(self, X, Y):
666
661
  X = X.reshape(X.shape[0], X.shape[1], 1)
@@ -684,12 +679,29 @@ class MultiOutputTsetlinMachine(CommonTsetlinMachine):
684
679
  return (self.score(X) >= 0).astype(np.uint32).transpose()
685
680
 
686
681
  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):
682
+ def __init__(
683
+ self, number_of_clauses,
684
+ T,
685
+ s,
686
+ q=1.0,
687
+ literal_budget=None,
688
+ no_clipping=False,
689
+ constant_update_p=0,
690
+ binary_inference=False,
691
+ and_group_normalization=False,
692
+ hierarchy_structure=((AND_GROUP, 1)),
693
+ boost_true_positive_feedback=1,
694
+ number_of_state_bits=8,
695
+ append_negated=True,
696
+ grid=(16*13,1,1),
697
+ block=(128,1,1),
698
+ seed=None
699
+ ):
688
700
  self.negative_clauses = 1
689
701
  self.tm_type = COALESCED_TM
690
702
  self.flip_polarity = 1
691
703
 
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)
704
+ super().__init__(number_of_clauses, T, s, q=q, literal_budget=literal_budget, 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
705
 
694
706
  def fit(self, X, Y):
695
707
  X = X.reshape(X.shape[0], X.shape[1], 1)
@@ -716,12 +728,16 @@ class MultiClassCoalescedTsetlinMachine(CommonTsetlinMachine):
716
728
  return np.argmax(self.score(X), axis=0)
717
729
 
718
730
  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):
731
+ def __init__(self, number_of_clauses, T, s, q=1.0, literal_budget=None, 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
732
  self.number_of_clauses = number_of_clauses
721
733
  self.T = T
722
734
  self.s = s
723
735
  self.q = q
724
- self.log_scale = log_scale
736
+ self.literal_budget = literal_budget
737
+ self.no_clipping = no_clipping
738
+ self.constant_update_p = constant_update_p
739
+ self.binary_inference = binary_inference
740
+ self.and_group_normalization = and_group_normalization
725
741
  self.weighted_clauses = weighted_clauses
726
742
  self.hierarchy_structure = hierarchy_structure
727
743
  self.boost_true_positive_feedback = boost_true_positive_feedback
@@ -739,7 +755,7 @@ class MultiClassTsetlinMachine:
739
755
  if not self.configured:
740
756
  self.tms = []
741
757
  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))
758
+ self.tms.append(TsetlinMachine(self.number_of_clauses, self.T, self.s, q=self.q, literal_budget=self.literal_budget, 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
759
 
744
760
  self.configured = True
745
761
 
@@ -760,7 +776,7 @@ class MultiClassTsetlinMachine:
760
776
  return
761
777
 
762
778
  def score(self, X):
763
- class_sums = np.empty((self.number_of_outputs, X.shape[0]), dtype=np.int32)
779
+ class_sums = np.empty((self.number_of_outputs, X.shape[0]), dtype=np.float32)
764
780
  for i in range(self.number_of_outputs):
765
781
  class_sums[i,:] = self.tms[i].score(X)
766
782
 
@@ -790,7 +806,7 @@ class MultiClassTsetlinMachine:
790
806
 
791
807
  def print_hierarchy(self, print_ta_state=False):
792
808
  for i in range(self.number_of_outputs):
793
- print("CLASS %d\n", i)
809
+ print("\nCLASS %d\n" % (i,))
794
810
  self.tms[i].print_hierarchy(print_ta_state = print_ta_state)
795
811
 
796
812
  def load(self, state_dict: dict):
@@ -805,7 +821,7 @@ class MultiClassTsetlinMachine:
805
821
  self.configured = True
806
822
 
807
823
  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):
824
+ def __init__(self, number_of_clauses, T, s, q=1.0, literal_budget=None, 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
825
  self.negative_clauses = 1
810
826
  self.flip_polarity = 0
811
827
 
@@ -814,7 +830,7 @@ class TsetlinMachine(CommonTsetlinMachine):
814
830
  else:
815
831
  self.tm_type = VANILLA_TM
816
832
 
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)
833
+ super().__init__(number_of_clauses, T, s, q=q, literal_budget=literal_budget, 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
834
 
819
835
  def fit(self, X, Y):
820
836
  X = X.reshape(X.shape[0], X.shape[1], 1)
@@ -838,11 +854,11 @@ class TsetlinMachine(CommonTsetlinMachine):
838
854
  return (self.score(X) >= 0).astype(np.int32)
839
855
 
840
856
  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):
857
+ def __init__(self, number_of_clauses, T, s, literal_budget=None, 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
858
  self.negative_clauses = 0
843
859
  self.flip_polarity = 0
844
860
 
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)
861
+ super().__init__(number_of_clauses, T, s, literal_budget=literal_budget, 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
862
 
847
863
  def fit(self, X, Y):
848
864
  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.3
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.3',
6
6
  author='Ole-Christoffer Granmo',
7
7
  author_email='ole.granmo@uia.no',
8
8
  url='https://github.com/cair/PyHierarchicalTsetlinMachineCUDA/',