addernet 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
addernet-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 AdderNet Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,4 @@
1
+ include addernet/src/*.c
2
+ include addernet/src/*.h
3
+ include README.md
4
+ include LICENSE
@@ -0,0 +1,153 @@
1
+ Metadata-Version: 2.4
2
+ Name: addernet
3
+ Version: 0.1.0
4
+ Summary: Neural network with zero multiplications at inference. AdderNet + Hyperdimensional Computing.
5
+ License: MIT
6
+ Project-URL: Homepage, https://github.com/addernet/addernet
7
+ Requires-Python: >=3.8
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Requires-Dist: numpy
11
+ Dynamic: license-file
12
+
13
+ # AdderNet
14
+
15
+ Rede neural que **não usa multiplicação** na inferência. Zero.
16
+
17
+ ```
18
+ Rede normal: resultado = entrada * peso + bias (mulsd + addsd)
19
+ AdderNet: resultado = tabela[entrada + offset] (1 acesso à memória)
20
+ ```
21
+
22
+ ## O que faz?
23
+
24
+ AdderNet aprende funções usando **apenas adição e subtração** durante o treinamento, e na hora de prever o resultado basta **ler uma posição da tabela** — nenhuma conta de verdade.
25
+
26
+ Funciona bem para funções de **uma variável**: converter Celsius em Fahrenheit, aprender `y = 3x + 7`, etc.
27
+
28
+ ## Instalação
29
+
30
+ **Requisitos**: GCC, Python 3.8+, numpy
31
+
32
+ ```bash
33
+ cd addernet_lib
34
+ make
35
+ ```
36
+
37
+ Isso gera `libaddernet.so`.
38
+
39
+ ## Uso (Python)
40
+
41
+ ```python
42
+ from addernet import AdderNetLayer
43
+
44
+ # Criar a rede
45
+ rede = AdderNetLayer(size=256, bias=50, input_min=-50, input_max=200, lr=0.1)
46
+
47
+ # Dados de treino: Celsius -> Fahrenheit
48
+ celsius = [0, 10, 20, 25, 30, 37, 50, 80, 100]
49
+ fahrenheit = [32, 50, 68, 77, 86, 98.6, 122, 176, 212]
50
+
51
+ # Treinar
52
+ rede.train(celsius, fahrenheit)
53
+
54
+ # Prever
55
+ print(rede.predict(37)) # 98.60
56
+ print(rede.predict(100)) # 212.00
57
+ ```
58
+
59
+ ### Salvar e carregar
60
+
61
+ ```python
62
+ # Salvar modelo treinado
63
+ rede.save("meu_modelo.bin")
64
+
65
+ # Carregar depois
66
+ rede = AdderNetLayer.load("meu_modelo.bin")
67
+ print(rede.predict(37)) # 98.60
68
+ ```
69
+
70
+ ### Previsão em lote (numpy)
71
+
72
+ ```python
73
+ import numpy as np
74
+
75
+ entradas = np.array([0, 25, 37, 100], dtype=np.float64)
76
+ saidas = rede.predict_batch(entradas)
77
+ # array([32.0, 77.0, 98.6, 212.0])
78
+ ```
79
+
80
+ ### Usando em C
81
+
82
+ ```c
83
+ #include "addernet.h"
84
+
85
+ an_layer *rede = an_layer_create(256, 50, -50, 200, 0.1);
86
+
87
+ double inputs[] = {0, 10, 25, 37, 100};
88
+ double targets[] = {32, 50, 77, 98.6, 212};
89
+ an_train(rede, inputs, targets, 5, 1000, 4000);
90
+
91
+ double resultado = an_predict(rede, 37.0); // 98.60
92
+
93
+ an_save(rede, "modelo.bin");
94
+ an_layer_free(rede);
95
+ ```
96
+
97
+ Compile com:
98
+ ```bash
99
+ gcc -O3 -o meu_programa meu_programa.c -L. -laddernet -lm
100
+ ```
101
+
102
+ ## Performance
103
+
104
+ | Método | Velocidade | vs Python puro |
105
+ |---|---|---|
106
+ | Python AdderNet (dict) | ~31K pred/s | 1x |
107
+ | C AdderNet (uma por uma via ctypes) | ~1.3M pred/s | ~42x |
108
+ | C AdderNet (lote numpy) | ~247M pred/s | ~8000x |
109
+
110
+ Para melhor performance em Python, use `predict_batch()` com arrays numpy.
111
+
112
+ ## Exemplos prontos
113
+
114
+ ```bash
115
+ cd addernet_lib
116
+
117
+ # Converter Celsius -> Fahrenheit
118
+ python3 examples/celsius_fahrenheit.py
119
+
120
+ # Aprender funções de adição
121
+ python3 examples/addition.py
122
+
123
+ # Benchmark de performance
124
+ python3 examples/benchmark.py
125
+ ```
126
+
127
+ ## Limitações
128
+
129
+ - Funciona para funções de **uma variável** (entrada inteira -> saída double)
130
+ - A entrada é truncada para int antes de indexar a tabela
131
+ - Para valores fora do range de treino, a precisão diminui (extrapolação linear)
132
+ - Não substitui redes neurais convencionais para problemas complexos (visão, linguagem, etc.)
133
+
134
+ ## Como funciona?
135
+
136
+ 1. **Treino**: Para cada amostra, testa se `peso + lr` ou `peso - lr` reduz o erro. Aplica a melhor direção.
137
+ 2. **Expansão**: Interpola entre os pontos de treino para gerar dados densos, e extrapola além das bordas usando a inclinação dos dois pontos mais próximos.
138
+ 3. **Inferência**: A tabela já tem o resultado final. É só acessar `tabela[(entrada + offset) & máscara]` — uma leitura de memória, zero aritmética.
139
+
140
+ ## Arquivos
141
+
142
+ ```
143
+ addernet_lib/
144
+ ├── addernet.h Cabeçalho da API
145
+ ├── addernet.c Implementação em C
146
+ ├── addernet.py Bindings Python (ctypes)
147
+ ├── Makefile Compilação
148
+ ├── test_main.c Teste em C
149
+ └── examples/
150
+ ├── celsius_fahrenheit.py
151
+ ├── addition.py
152
+ └── benchmark.py
153
+ ```
@@ -0,0 +1,141 @@
1
+ # AdderNet
2
+
3
+ Rede neural que **não usa multiplicação** na inferência. Zero.
4
+
5
+ ```
6
+ Rede normal: resultado = entrada * peso + bias (mulsd + addsd)
7
+ AdderNet: resultado = tabela[entrada + offset] (1 acesso à memória)
8
+ ```
9
+
10
+ ## O que faz?
11
+
12
+ AdderNet aprende funções usando **apenas adição e subtração** durante o treinamento, e na hora de prever o resultado basta **ler uma posição da tabela** — nenhuma conta de verdade.
13
+
14
+ Funciona bem para funções de **uma variável**: converter Celsius em Fahrenheit, aprender `y = 3x + 7`, etc.
15
+
16
+ ## Instalação
17
+
18
+ **Requisitos**: GCC, Python 3.8+, numpy
19
+
20
+ ```bash
21
+ cd addernet_lib
22
+ make
23
+ ```
24
+
25
+ Isso gera `libaddernet.so`.
26
+
27
+ ## Uso (Python)
28
+
29
+ ```python
30
+ from addernet import AdderNetLayer
31
+
32
+ # Criar a rede
33
+ rede = AdderNetLayer(size=256, bias=50, input_min=-50, input_max=200, lr=0.1)
34
+
35
+ # Dados de treino: Celsius -> Fahrenheit
36
+ celsius = [0, 10, 20, 25, 30, 37, 50, 80, 100]
37
+ fahrenheit = [32, 50, 68, 77, 86, 98.6, 122, 176, 212]
38
+
39
+ # Treinar
40
+ rede.train(celsius, fahrenheit)
41
+
42
+ # Prever
43
+ print(rede.predict(37)) # 98.60
44
+ print(rede.predict(100)) # 212.00
45
+ ```
46
+
47
+ ### Salvar e carregar
48
+
49
+ ```python
50
+ # Salvar modelo treinado
51
+ rede.save("meu_modelo.bin")
52
+
53
+ # Carregar depois
54
+ rede = AdderNetLayer.load("meu_modelo.bin")
55
+ print(rede.predict(37)) # 98.60
56
+ ```
57
+
58
+ ### Previsão em lote (numpy)
59
+
60
+ ```python
61
+ import numpy as np
62
+
63
+ entradas = np.array([0, 25, 37, 100], dtype=np.float64)
64
+ saidas = rede.predict_batch(entradas)
65
+ # array([32.0, 77.0, 98.6, 212.0])
66
+ ```
67
+
68
+ ### Usando em C
69
+
70
+ ```c
71
+ #include "addernet.h"
72
+
73
+ an_layer *rede = an_layer_create(256, 50, -50, 200, 0.1);
74
+
75
+ double inputs[] = {0, 10, 25, 37, 100};
76
+ double targets[] = {32, 50, 77, 98.6, 212};
77
+ an_train(rede, inputs, targets, 5, 1000, 4000);
78
+
79
+ double resultado = an_predict(rede, 37.0); // 98.60
80
+
81
+ an_save(rede, "modelo.bin");
82
+ an_layer_free(rede);
83
+ ```
84
+
85
+ Compile com:
86
+ ```bash
87
+ gcc -O3 -o meu_programa meu_programa.c -L. -laddernet -lm
88
+ ```
89
+
90
+ ## Performance
91
+
92
+ | Método | Velocidade | vs Python puro |
93
+ |---|---|---|
94
+ | Python AdderNet (dict) | ~31K pred/s | 1x |
95
+ | C AdderNet (uma por uma via ctypes) | ~1.3M pred/s | ~42x |
96
+ | C AdderNet (lote numpy) | ~247M pred/s | ~8000x |
97
+
98
+ Para melhor performance em Python, use `predict_batch()` com arrays numpy.
99
+
100
+ ## Exemplos prontos
101
+
102
+ ```bash
103
+ cd addernet_lib
104
+
105
+ # Converter Celsius -> Fahrenheit
106
+ python3 examples/celsius_fahrenheit.py
107
+
108
+ # Aprender funções de adição
109
+ python3 examples/addition.py
110
+
111
+ # Benchmark de performance
112
+ python3 examples/benchmark.py
113
+ ```
114
+
115
+ ## Limitações
116
+
117
+ - Funciona para funções de **uma variável** (entrada inteira -> saída double)
118
+ - A entrada é truncada para int antes de indexar a tabela
119
+ - Para valores fora do range de treino, a precisão diminui (extrapolação linear)
120
+ - Não substitui redes neurais convencionais para problemas complexos (visão, linguagem, etc.)
121
+
122
+ ## Como funciona?
123
+
124
+ 1. **Treino**: Para cada amostra, testa se `peso + lr` ou `peso - lr` reduz o erro. Aplica a melhor direção.
125
+ 2. **Expansão**: Interpola entre os pontos de treino para gerar dados densos, e extrapola além das bordas usando a inclinação dos dois pontos mais próximos.
126
+ 3. **Inferência**: A tabela já tem o resultado final. É só acessar `tabela[(entrada + offset) & máscara]` — uma leitura de memória, zero aritmética.
127
+
128
+ ## Arquivos
129
+
130
+ ```
131
+ addernet_lib/
132
+ ├── addernet.h Cabeçalho da API
133
+ ├── addernet.c Implementação em C
134
+ ├── addernet.py Bindings Python (ctypes)
135
+ ├── Makefile Compilação
136
+ ├── test_main.c Teste em C
137
+ └── examples/
138
+ ├── celsius_fahrenheit.py
139
+ ├── addition.py
140
+ └── benchmark.py
141
+ ```
@@ -0,0 +1,29 @@
1
+ import os
2
+ import sys
3
+ from pathlib import Path
4
+
5
+ _HERE = Path(__file__).parent
6
+
7
+ if sys.platform == "darwin":
8
+ _lib_hdc_name = "libaddernet_hdc.dylib"
9
+ _lib_addernet_name = "libaddernet.dylib"
10
+ elif sys.platform == "win32":
11
+ _lib_hdc_name = "addernet_hdc.dll"
12
+ _lib_addernet_name = "addernet.dll"
13
+ else:
14
+ _lib_hdc_name = "libaddernet_hdc.so"
15
+ _lib_addernet_name = "libaddernet.so"
16
+
17
+ _need_build = False
18
+ if not (_HERE / _lib_hdc_name).exists() or not (_HERE / _lib_addernet_name).exists():
19
+ _need_build = True
20
+
21
+ if _need_build:
22
+ from . import build_ext
23
+ build_ext.build()
24
+
25
+ from .addernet import AdderNetLayer
26
+ from .addernet_hdc import AdderNetHDC
27
+
28
+ __version__ = "0.1.0"
29
+ __all__ = ["AdderNetLayer", "AdderNetHDC"]
@@ -0,0 +1,203 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ AdderNet Python Bindings — ctypes interface to libaddernet.so
4
+ ==============================================================
5
+
6
+ Usage:
7
+ from addernet import AdderNetLayer
8
+
9
+ layer = AdderNetLayer(size=256, bias=50, input_min=-50, input_max=200, lr=0.1)
10
+ layer.train(inputs, targets, epochs_raw=1000, epochs_expanded=4000)
11
+ result = layer.predict(37.0)
12
+ layer.save("model.bin")
13
+ """
14
+
15
+ import os
16
+ import ctypes
17
+ import numpy as np
18
+
19
+ # ---- Locate shared library ----
20
+
21
+ _HERE = os.path.dirname(os.path.abspath(__file__))
22
+ _LIB_NAMES = [
23
+ os.path.join(_HERE, "libaddernet.so"),
24
+ os.path.join(_HERE, "libaddernet.dylib"),
25
+ "libaddernet.so",
26
+ ]
27
+
28
+ _lib = None
29
+ for _name in _LIB_NAMES:
30
+ try:
31
+ _lib = ctypes.CDLL(_name)
32
+ break
33
+ except OSError:
34
+ continue
35
+
36
+ if _lib is None:
37
+ raise OSError(
38
+ "Cannot find libaddernet.so. "
39
+ "Build it first: cd addernet_lib && make"
40
+ )
41
+
42
+ # ---- Opaque pointer type ----
43
+
44
+ _AnLayerPtr = ctypes.c_void_p
45
+
46
+ # ---- Function signatures ----
47
+
48
+ _lib.an_layer_create.restype = _AnLayerPtr
49
+ _lib.an_layer_create.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_double]
50
+
51
+ _lib.an_layer_free.restype = None
52
+ _lib.an_layer_free.argtypes = [_AnLayerPtr]
53
+
54
+ _lib.an_train.restype = ctypes.c_int
55
+ _lib.an_train.argtypes = [
56
+ _AnLayerPtr,
57
+ ctypes.POINTER(ctypes.c_double),
58
+ ctypes.POINTER(ctypes.c_double),
59
+ ctypes.c_int,
60
+ ctypes.c_int,
61
+ ctypes.c_int,
62
+ ]
63
+
64
+ _lib.an_predict.restype = ctypes.c_double
65
+ _lib.an_predict.argtypes = [_AnLayerPtr, ctypes.c_double]
66
+
67
+ _lib.an_predict_batch.restype = ctypes.c_int
68
+ _lib.an_predict_batch.argtypes = [
69
+ _AnLayerPtr,
70
+ ctypes.POINTER(ctypes.c_double),
71
+ ctypes.POINTER(ctypes.c_double),
72
+ ctypes.c_int,
73
+ ]
74
+
75
+ _lib.an_save.restype = ctypes.c_int
76
+ _lib.an_save.argtypes = [_AnLayerPtr, ctypes.c_char_p]
77
+
78
+ _lib.an_load.restype = _AnLayerPtr
79
+ _lib.an_load.argtypes = [ctypes.c_char_p]
80
+
81
+ _lib.an_get_offset.restype = ctypes.c_int
82
+ _lib.an_get_offset.argtypes = [_AnLayerPtr, ctypes.POINTER(ctypes.c_double), ctypes.c_int]
83
+
84
+ _lib.an_get_size.restype = ctypes.c_int
85
+ _lib.an_get_size.argtypes = [_AnLayerPtr]
86
+
87
+ _lib.an_get_bias.restype = ctypes.c_int
88
+ _lib.an_get_bias.argtypes = [_AnLayerPtr]
89
+
90
+ _lib.an_get_input_min.restype = ctypes.c_int
91
+ _lib.an_get_input_min.argtypes = [_AnLayerPtr]
92
+
93
+ _lib.an_get_input_max.restype = ctypes.c_int
94
+ _lib.an_get_input_max.argtypes = [_AnLayerPtr]
95
+
96
+ _lib.an_get_lr.restype = ctypes.c_double
97
+ _lib.an_get_lr.argtypes = [_AnLayerPtr]
98
+
99
+
100
+ # ---- Python wrapper ----
101
+
102
+ class AdderNetLayer:
103
+ """
104
+ Python wrapper around the C an_layer struct.
105
+ Provides train / predict / predict_batch / save / load using numpy arrays.
106
+ """
107
+
108
+ def __init__(self, size=256, bias=50, input_min=-50, input_max=200, lr=0.1,
109
+ _ptr=None):
110
+ if _ptr is not None:
111
+ self._ptr = _ptr
112
+ else:
113
+ self._ptr = _lib.an_layer_create(size, bias, input_min, input_max, lr)
114
+ if not self._ptr:
115
+ raise MemoryError("an_layer_create failed")
116
+
117
+ def __del__(self):
118
+ if hasattr(self, "_ptr") and self._ptr:
119
+ _lib.an_layer_free(self._ptr)
120
+ self._ptr = None
121
+
122
+ def train(self, inputs, targets, epochs_raw=1000, epochs_expanded=4000):
123
+ """Train on input→target pairs. Accepts lists or numpy arrays."""
124
+ inputs = np.ascontiguousarray(inputs, dtype=np.float64)
125
+ targets = np.ascontiguousarray(targets, dtype=np.float64)
126
+ n = len(inputs)
127
+ if len(targets) != n:
128
+ raise ValueError("inputs and targets must have same length")
129
+ ret = _lib.an_train(
130
+ self._ptr,
131
+ inputs.ctypes.data_as(ctypes.POINTER(ctypes.c_double)),
132
+ targets.ctypes.data_as(ctypes.POINTER(ctypes.c_double)),
133
+ n, epochs_raw, epochs_expanded,
134
+ )
135
+ if ret != 0:
136
+ raise RuntimeError("an_train failed")
137
+
138
+ def predict(self, x):
139
+ """Single prediction. Returns float."""
140
+ return _lib.an_predict(self._ptr, float(x))
141
+
142
+ def predict_batch(self, inputs):
143
+ """Batch prediction. Accepts numpy array, returns numpy array."""
144
+ inputs = np.ascontiguousarray(inputs, dtype=np.float64)
145
+ n = len(inputs)
146
+ outputs = np.empty(n, dtype=np.float64)
147
+ _lib.an_predict_batch(
148
+ self._ptr,
149
+ inputs.ctypes.data_as(ctypes.POINTER(ctypes.c_double)),
150
+ outputs.ctypes.data_as(ctypes.POINTER(ctypes.c_double)),
151
+ n,
152
+ )
153
+ return outputs
154
+
155
+ def save(self, path):
156
+ """Save layer to binary file."""
157
+ ret = _lib.an_save(self._ptr, path.encode("utf-8"))
158
+ if ret != 0:
159
+ raise IOError(f"an_save failed: {path}")
160
+
161
+ @classmethod
162
+ def load(cls, path):
163
+ """Load layer from binary file. Returns new AdderNetLayer."""
164
+ ptr = _lib.an_load(path.encode("utf-8"))
165
+ if not ptr:
166
+ raise IOError(f"an_load failed: {path}")
167
+ return cls(_ptr=ptr)
168
+
169
+ @property
170
+ def offset_table(self):
171
+ """Return the offset table as a numpy array."""
172
+ n = _lib.an_get_size(self._ptr)
173
+ buf = np.empty(n, dtype=np.float64)
174
+ _lib.an_get_offset(
175
+ self._ptr,
176
+ buf.ctypes.data_as(ctypes.POINTER(ctypes.c_double)),
177
+ n,
178
+ )
179
+ return buf
180
+
181
+ @property
182
+ def size(self):
183
+ return _lib.an_get_size(self._ptr)
184
+
185
+ @property
186
+ def bias(self):
187
+ return _lib.an_get_bias(self._ptr)
188
+
189
+ @property
190
+ def input_min(self):
191
+ return _lib.an_get_input_min(self._ptr)
192
+
193
+ @property
194
+ def input_max(self):
195
+ return _lib.an_get_input_max(self._ptr)
196
+
197
+ @property
198
+ def lr(self):
199
+ return _lib.an_get_lr(self._ptr)
200
+
201
+ def __repr__(self):
202
+ return (f"AdderNetLayer(size={self.size}, bias={self.bias}, "
203
+ f"range=[{self.input_min},{self.input_max}], lr={self.lr})")