creating-intelligence 0.1.0a7__py3-none-win_amd64.whl
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.
- creating_intelligence/__init__.py +41 -0
- creating_intelligence/circuits.py +1945 -0
- creating_intelligence/libmemory.pyd +0 -0
- creating_intelligence/memory_c_ffi.py +170 -0
- creating_intelligence/memory_python.py +143 -0
- creating_intelligence-0.1.0a7.dist-info/METADATA +55 -0
- creating_intelligence-0.1.0a7.dist-info/RECORD +9 -0
- creating_intelligence-0.1.0a7.dist-info/WHEEL +5 -0
- creating_intelligence-0.1.0a7.dist-info/top_level.txt +1 -0
|
Binary file
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Copyright (c) 2026 Peter Overmann
|
|
3
|
+
|
|
4
|
+
SPDX-License-Identifier: MIT
|
|
5
|
+
|
|
6
|
+
This file is part of the "Creating Intelligence" project. It is licensed
|
|
7
|
+
under the MIT License. You may obtain a copy of the License in the LICENSE
|
|
8
|
+
file in the root directory of this repository.
|
|
9
|
+
|
|
10
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
11
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
12
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import ctypes
|
|
16
|
+
import os
|
|
17
|
+
import sys
|
|
18
|
+
import importlib.machinery
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
|
|
21
|
+
def _candidate_paths():
|
|
22
|
+
"""Yield possible locations of the compiled C library, most preferred first."""
|
|
23
|
+
# 1. Explicit override, e.g. MEMORY_LIB_PATH=/path/to/libmemory.so
|
|
24
|
+
override = os.environ.get("MEMORY_LIB_PATH")
|
|
25
|
+
if override:
|
|
26
|
+
yield Path(override)
|
|
27
|
+
|
|
28
|
+
# 2. Extension built by pip/setuptools, under any suffix this interpreter
|
|
29
|
+
# accepts (.cpython-312-x86_64-linux-gnu.so, .abi3.so, .cp312-win_amd64.pyd, ...)
|
|
30
|
+
pkg_dir = Path(__file__).resolve().parent
|
|
31
|
+
for suffix in importlib.machinery.EXTENSION_SUFFIXES:
|
|
32
|
+
yield pkg_dir / f"libmemory{suffix}"
|
|
33
|
+
|
|
34
|
+
# 3. Local development build from the C Makefile (<repo>/c/lib/)
|
|
35
|
+
if sys.platform == "darwin":
|
|
36
|
+
lib_name = "libmemory.dylib"
|
|
37
|
+
elif sys.platform == "win32":
|
|
38
|
+
lib_name = "libmemory.dll"
|
|
39
|
+
else:
|
|
40
|
+
lib_name = "libmemory.so"
|
|
41
|
+
yield pkg_dir.parent.parent / "c" / "lib" / lib_name
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _load_library():
|
|
45
|
+
tried = []
|
|
46
|
+
for path in _candidate_paths():
|
|
47
|
+
if not path.is_file():
|
|
48
|
+
tried.append(f" {path} (not found)")
|
|
49
|
+
continue
|
|
50
|
+
try:
|
|
51
|
+
return ctypes.CDLL(str(path))
|
|
52
|
+
except OSError as e:
|
|
53
|
+
tried.append(f" {path} ({e})")
|
|
54
|
+
raise OSError("Could not load the C memory library. Tried:\n" + "\n".join(tried))
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
lib = _load_library()
|
|
58
|
+
|
|
59
|
+
# Define the C 'Set' struct in Python.
|
|
60
|
+
class CSet(ctypes.Structure):
|
|
61
|
+
_fields_ = [
|
|
62
|
+
("a", ctypes.POINTER(ctypes.c_int)),
|
|
63
|
+
("n", ctypes.c_int),
|
|
64
|
+
("p", ctypes.c_int)
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
# Define FFI signatures mapped from the C implementations.
|
|
68
|
+
lib.Memory_new.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int]
|
|
69
|
+
lib.Memory_new.restype = ctypes.c_void_p
|
|
70
|
+
|
|
71
|
+
lib.Memory_set_threshold.argtypes = [ctypes.c_void_p, ctypes.c_int]
|
|
72
|
+
lib.Memory_set_threshold.restype = None
|
|
73
|
+
|
|
74
|
+
lib.Memory_get_threshold.argtypes = [ctypes.c_void_p]
|
|
75
|
+
lib.Memory_get_threshold.restype = ctypes.c_int
|
|
76
|
+
|
|
77
|
+
lib.Memory_set_fraction.argtypes = [ctypes.c_void_p, ctypes.c_double]
|
|
78
|
+
lib.Memory_set_fraction.restype = None
|
|
79
|
+
|
|
80
|
+
lib.Memory_get_fraction.argtypes = [ctypes.c_void_p]
|
|
81
|
+
lib.Memory_get_fraction.restype = ctypes.c_double
|
|
82
|
+
|
|
83
|
+
lib.Memory_count.argtypes = [ctypes.c_void_p]
|
|
84
|
+
lib.Memory_count.restype = ctypes.c_int64
|
|
85
|
+
|
|
86
|
+
lib.Memory_free.argtypes = [ctypes.c_void_p]
|
|
87
|
+
lib.Memory_free.restype = None
|
|
88
|
+
|
|
89
|
+
lib.Memory_write.argtypes = [ctypes.c_void_p, ctypes.POINTER(CSet), ctypes.POINTER(CSet)]
|
|
90
|
+
lib.Memory_write.restype = None
|
|
91
|
+
|
|
92
|
+
lib.Memory_read.argtypes = [ctypes.c_void_p, ctypes.POINTER(CSet), ctypes.POINTER(CSet)]
|
|
93
|
+
lib.Memory_read.restype = ctypes.POINTER(CSet)
|
|
94
|
+
|
|
95
|
+
def Memory(config: dict) -> dict:
|
|
96
|
+
NA, PA = config["A_parameters"]
|
|
97
|
+
NB, PB = config["B_parameters"]
|
|
98
|
+
|
|
99
|
+
# Construct the underlying C memory object.
|
|
100
|
+
M = lib.Memory_new(NA, PA, NB, PB)
|
|
101
|
+
|
|
102
|
+
# Push user-defined threshold scaling if provided.
|
|
103
|
+
if config.get("threshold") is not None:
|
|
104
|
+
scaled_threshold = round(config.get("threshold") * PA)
|
|
105
|
+
lib.Memory_set_threshold(M, scaled_threshold)
|
|
106
|
+
|
|
107
|
+
# Push optional relative threshold (fraction of the query population).
|
|
108
|
+
if config.get("fraction") is not None:
|
|
109
|
+
lib.Memory_set_fraction(M, float(config.get("fraction")))
|
|
110
|
+
|
|
111
|
+
# Retrieve final absolute threshold from C backend.
|
|
112
|
+
T = lib.Memory_get_threshold(M)
|
|
113
|
+
|
|
114
|
+
# Retrieve fraction parameter from C backend
|
|
115
|
+
fraction = lib.Memory_get_fraction(M)
|
|
116
|
+
|
|
117
|
+
def _make_cset(data_list, dimension):
|
|
118
|
+
"""Creates a CSet and holds the underlying array reference to prevent garbage collection."""
|
|
119
|
+
length = len(data_list)
|
|
120
|
+
shifted_data = [x - 1 for x in data_list]
|
|
121
|
+
arr = (ctypes.c_int * length)(*shifted_data)
|
|
122
|
+
cset = CSet(a=ctypes.cast(arr, ctypes.POINTER(ctypes.c_int)), n=dimension, p=length)
|
|
123
|
+
return cset, arr
|
|
124
|
+
|
|
125
|
+
def store(A, B=None):
|
|
126
|
+
# Store auto-association A -> A in memory if B is omitted.
|
|
127
|
+
if B is None:
|
|
128
|
+
B = A
|
|
129
|
+
|
|
130
|
+
if len(A) > 0 and len(B) > 0:
|
|
131
|
+
cset_A, _ref_A = _make_cset(A, NA)
|
|
132
|
+
cset_B, _ref_B = _make_cset(B, NB)
|
|
133
|
+
lib.Memory_write(M, ctypes.byref(cset_A), ctypes.byref(cset_B))
|
|
134
|
+
|
|
135
|
+
def retrieve(A):
|
|
136
|
+
if len(A) == 0:
|
|
137
|
+
return []
|
|
138
|
+
|
|
139
|
+
cset_A, _ref_A = _make_cset(A, NA)
|
|
140
|
+
|
|
141
|
+
# Allocate space for retrieved values up to max dimension NB.
|
|
142
|
+
Y_arr = (ctypes.c_int * NB)()
|
|
143
|
+
cset_Y = CSet(a=ctypes.cast(Y_arr, ctypes.POINTER(ctypes.c_int)), n=NB, p=0)
|
|
144
|
+
|
|
145
|
+
# Memory_read returns a pointer to the populated result Set.
|
|
146
|
+
res_ptr = lib.Memory_read(M, ctypes.byref(cset_A), ctypes.byref(cset_Y))
|
|
147
|
+
|
|
148
|
+
if not res_ptr:
|
|
149
|
+
return []
|
|
150
|
+
|
|
151
|
+
res_set = res_ptr.contents
|
|
152
|
+
return [res_set.a[i] + 1 for i in range(res_set.p)]
|
|
153
|
+
|
|
154
|
+
def memorycount():
|
|
155
|
+
return lib.Memory_count(M)
|
|
156
|
+
|
|
157
|
+
def clear():
|
|
158
|
+
lib.Memory_free(M)
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
"A_parameters": (NA, PA),
|
|
162
|
+
"B_parameters": (NB, PB),
|
|
163
|
+
"T": T,
|
|
164
|
+
"fraction": fraction,
|
|
165
|
+
"store": store,
|
|
166
|
+
"retrieve": retrieve,
|
|
167
|
+
"clear": clear,
|
|
168
|
+
"memorycount": memorycount,
|
|
169
|
+
"backend": "c_ffi"
|
|
170
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Copyright (c) 2026 Peter Overmann
|
|
3
|
+
|
|
4
|
+
SPDX-License-Identifier: MIT
|
|
5
|
+
|
|
6
|
+
This file is part of the "Creating Intelligence" project. It is licensed
|
|
7
|
+
under the MIT License. You may obtain a copy of the License in the LICENSE
|
|
8
|
+
file in the root directory of this repository.
|
|
9
|
+
|
|
10
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
11
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
12
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import math
|
|
16
|
+
import numpy as np
|
|
17
|
+
from itertools import combinations
|
|
18
|
+
|
|
19
|
+
def Memory(config: dict) -> dict:
|
|
20
|
+
# Extract hyperparameters
|
|
21
|
+
NA, PA = config["A_parameters"]
|
|
22
|
+
NB, PB = config["B_parameters"]
|
|
23
|
+
|
|
24
|
+
# Memory capacity, needed for default threshold calculation
|
|
25
|
+
if NA == NB and PA == PB:
|
|
26
|
+
capacity = round((math.log(2.0) * NA * (NA - 1) * (NA - 2)) /
|
|
27
|
+
(PA * (PA - 1) * (PA - 2)))
|
|
28
|
+
else:
|
|
29
|
+
capacity = round((math.log(2.0) * NA * (NA - 1) * NB) /
|
|
30
|
+
(PA * (PA - 1) * PB))
|
|
31
|
+
|
|
32
|
+
# Default pattern matching threshold
|
|
33
|
+
T = 1
|
|
34
|
+
while (T < PA and
|
|
35
|
+
(capacity**2 * math.comb(PA, T) *
|
|
36
|
+
math.comb(NA - PA, PA - T) /
|
|
37
|
+
math.comb(NA, PA)) >= 1):
|
|
38
|
+
T += 1
|
|
39
|
+
|
|
40
|
+
# User-defined (scaled) threshold via get()
|
|
41
|
+
if config.get("threshold") is not None:
|
|
42
|
+
T = round(config["threshold"] * PA)
|
|
43
|
+
|
|
44
|
+
T = max(2, T)
|
|
45
|
+
|
|
46
|
+
# Optional relative threshold: fraction of the query population that must
|
|
47
|
+
# match (1.0 = all input bits). Absent or 0 keeps the fixed threshold T.
|
|
48
|
+
fraction = config.get("fraction") or 0.0
|
|
49
|
+
|
|
50
|
+
# Initialize enclosed memory dictionary and zero array
|
|
51
|
+
mem = {}
|
|
52
|
+
zero = np.zeros(NB + 1, dtype=np.int8)
|
|
53
|
+
|
|
54
|
+
def store(A, B=None):
|
|
55
|
+
if B is None:
|
|
56
|
+
B = A
|
|
57
|
+
|
|
58
|
+
# Empty patterns store nothing
|
|
59
|
+
if not A or not B:
|
|
60
|
+
return
|
|
61
|
+
|
|
62
|
+
v = zero.copy()
|
|
63
|
+
v[B] = 1
|
|
64
|
+
|
|
65
|
+
for pair in combinations(A, 2):
|
|
66
|
+
key = tuple(sorted(pair))
|
|
67
|
+
|
|
68
|
+
if key not in mem:
|
|
69
|
+
mem[key] = v.copy()
|
|
70
|
+
else:
|
|
71
|
+
mem[key] = np.bitwise_or(mem[key], v)
|
|
72
|
+
|
|
73
|
+
def retrieve(A):
|
|
74
|
+
|
|
75
|
+
X = A
|
|
76
|
+
|
|
77
|
+
# Per-query threshold, at least the fixed threshold T
|
|
78
|
+
Tq = max(T, math.ceil(fraction * len(X) - 1e-9))
|
|
79
|
+
|
|
80
|
+
while True:
|
|
81
|
+
P = len(X)
|
|
82
|
+
if P < Tq:
|
|
83
|
+
return []
|
|
84
|
+
|
|
85
|
+
Ri = np.zeros((P, NB + 1), dtype=np.int32)
|
|
86
|
+
|
|
87
|
+
for i, j in combinations(range(P), 2):
|
|
88
|
+
key = tuple(sorted([X[i], X[j]]))
|
|
89
|
+
v = mem.get(key, zero)
|
|
90
|
+
Ri[i] += v
|
|
91
|
+
Ri[j] += v
|
|
92
|
+
|
|
93
|
+
R = np.sum(Ri, axis=0) // 2
|
|
94
|
+
|
|
95
|
+
R_valid = R[1:]
|
|
96
|
+
sorted_R = np.sort(R_valid)
|
|
97
|
+
|
|
98
|
+
t_val = sorted_R[-PB] if PB <= len(sorted_R) else sorted_R[0]
|
|
99
|
+
t = max(1, t_val)
|
|
100
|
+
|
|
101
|
+
if t < (Tq * (Tq - 1)) / 2:
|
|
102
|
+
return []
|
|
103
|
+
|
|
104
|
+
Y = [i for i in range(1, NB + 1) if R[i] >= t]
|
|
105
|
+
w = np.sum(Ri[:, Y], axis=1)
|
|
106
|
+
|
|
107
|
+
ws = np.sort(w)
|
|
108
|
+
h = P
|
|
109
|
+
|
|
110
|
+
while h > 0 and ws[-h] < len(Y) * (h - 1):
|
|
111
|
+
h -= 1
|
|
112
|
+
|
|
113
|
+
if h < Tq:
|
|
114
|
+
return []
|
|
115
|
+
|
|
116
|
+
cutoff = ws[-h]
|
|
117
|
+
new_X = [X[i] for i in range(P) if w[i] >= cutoff]
|
|
118
|
+
|
|
119
|
+
if h == Tq and h < len(new_X):
|
|
120
|
+
return []
|
|
121
|
+
|
|
122
|
+
if len(new_X) == P:
|
|
123
|
+
return Y
|
|
124
|
+
|
|
125
|
+
X = new_X
|
|
126
|
+
|
|
127
|
+
def memorycount():
|
|
128
|
+
return sum(np.sum(v) for v in mem.values())
|
|
129
|
+
|
|
130
|
+
def clear():
|
|
131
|
+
mem.clear()
|
|
132
|
+
|
|
133
|
+
return {
|
|
134
|
+
"A_parameters": (NA, PA),
|
|
135
|
+
"B_parameters": (NB, PB),
|
|
136
|
+
"T": T,
|
|
137
|
+
"fraction": fraction,
|
|
138
|
+
"store": store,
|
|
139
|
+
"retrieve": retrieve,
|
|
140
|
+
"clear": clear,
|
|
141
|
+
"memorycount": memorycount,
|
|
142
|
+
"backend": "python"
|
|
143
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: creating-intelligence
|
|
3
|
+
Version: 0.1.0a7
|
|
4
|
+
Summary: A computational foundation for AGI, based on hyperdimensional sparse data and framed in set theory.
|
|
5
|
+
Author-email: Peter Overmann <mail@creatingintelligence.org>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://creatingintelligence.org
|
|
8
|
+
Project-URL: Documentation, https://creatingintelligence.org/docs
|
|
9
|
+
Project-URL: Repository, https://github.com/PeterOvermann/CreatingIntelligence
|
|
10
|
+
Keywords: artificial intelligence,computational theory of mind,hyperdimensional computing,sparse distributed representations,topological associative memory,subset matching algorithm,spiking neural networks,neuromorphic computing,theoretical neuroscience
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: C
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Requires-Python: >=3.9
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
Requires-Dist: matplotlib>=3.5.0
|
|
17
|
+
Requires-Dist: networkx>=2.8.0
|
|
18
|
+
Requires-Dist: numpy>=1.21.0
|
|
19
|
+
Requires-Dist: scipy>=1.8.0
|
|
20
|
+
Provides-Extra: viz
|
|
21
|
+
Requires-Dist: pydot>=1.4.2; extra == "viz"
|
|
22
|
+
Provides-Extra: all
|
|
23
|
+
Requires-Dist: creating-intelligence[viz]; extra == "all"
|
|
24
|
+
|
|
25
|
+
# Creating Intelligence
|
|
26
|
+
|
|
27
|
+
A computational foundation for AGI, based on hyperdimensional sparse data and framed in set theory.
|
|
28
|
+
|
|
29
|
+
The mind performs computations on sparse binary data using Sparse Distributed Representations (SDRs) for perceptual information and Sparse Holographic Representations (SHRs) for symbols. The centerpiece of this project is Topological Associative Memory — a newly discovered associative memory for sparse data, serving as the computational kernel that models the brain's core algorithm.
|
|
30
|
+
|
|
31
|
+
### Links
|
|
32
|
+
|
|
33
|
+
* **Website:** https://creatingintelligence.org
|
|
34
|
+
* **Documentation:** https://creatingintelligence.org/docs
|
|
35
|
+
* **GitHub Repository:** https://github.com/PeterOvermann/CreatingIntelligence
|
|
36
|
+
* **Preprint:** [arXiv:2606.31819](https://arxiv.org/abs/2606.31819)
|
|
37
|
+
|
|
38
|
+
### Installation
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pip install creating-intelligence
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
#### Optional installations
|
|
46
|
+
|
|
47
|
+
| Extra | Installs | Purpose |
|
|
48
|
+
| ----- | -------- | ------- |
|
|
49
|
+
| `viz` | `pydot` | Left-to-right layouts when drawing circuit diagrams |
|
|
50
|
+
| `all` | every extra above | Everything optional in one step |
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pip install "creating-intelligence[viz]"
|
|
54
|
+
pip install "creating-intelligence[all]"
|
|
55
|
+
```
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
creating_intelligence/__init__.py,sha256=-PCADoMkzPtoP2yBYxCKs2wUVL38IqLR8KIG0PtEpOE,1309
|
|
2
|
+
creating_intelligence/circuits.py,sha256=WgQvS0cWxwKfo-tHQzHttWqfTTglQ3Zv8czhJyu0G9c,59299
|
|
3
|
+
creating_intelligence/libmemory.pyd,sha256=VkPrWUIrrs2ieqYH8tuEHs9xhxKDVGZwUKYmpS5gmOk,19968
|
|
4
|
+
creating_intelligence/memory_c_ffi.py,sha256=PLPssHaIE2CFJ48XavCFe18be2QWKF45cAP-F_34bco,5910
|
|
5
|
+
creating_intelligence/memory_python.py,sha256=sWayWH0_h15XiJyb5OjNO94-hf9JqWYmoU_s4GfG2e0,4096
|
|
6
|
+
creating_intelligence-0.1.0a7.dist-info/METADATA,sha256=6BZCMsUIVWornAN0OLI7IbTyFYTOi7Y0XHFbWzTdcns,2404
|
|
7
|
+
creating_intelligence-0.1.0a7.dist-info/WHEEL,sha256=Zx98gwb_dQKckJK3HEOVKnxxD52EfU_PXDG_usMJ2ng,98
|
|
8
|
+
creating_intelligence-0.1.0a7.dist-info/top_level.txt,sha256=H5O7jHPu13cfTnlha8xX5GjLNEwY_9NAXlO401214ds,22
|
|
9
|
+
creating_intelligence-0.1.0a7.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
creating_intelligence
|