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 +21 -0
- addernet-0.1.0/MANIFEST.in +4 -0
- addernet-0.1.0/PKG-INFO +153 -0
- addernet-0.1.0/README.md +141 -0
- addernet-0.1.0/addernet/__init__.py +29 -0
- addernet-0.1.0/addernet/addernet.py +203 -0
- addernet-0.1.0/addernet/addernet_hdc.py +280 -0
- addernet-0.1.0/addernet/build_ext.py +42 -0
- addernet-0.1.0/addernet/libaddernet.so +0 -0
- addernet-0.1.0/addernet/libaddernet_hdc.so +0 -0
- addernet-0.1.0/addernet/src/addernet.c +228 -0
- addernet-0.1.0/addernet/src/addernet.h +136 -0
- addernet-0.1.0/addernet/src/addernet_hdc.c +273 -0
- addernet-0.1.0/addernet/src/addernet_hdc.h +117 -0
- addernet-0.1.0/addernet/src/hdc_core.c +145 -0
- addernet-0.1.0/addernet/src/hdc_core.h +83 -0
- addernet-0.1.0/addernet/src/hdc_core_cuda.c +175 -0
- addernet-0.1.0/addernet/src/hdc_cuda_batch.c +552 -0
- addernet-0.1.0/addernet.egg-info/PKG-INFO +153 -0
- addernet-0.1.0/addernet.egg-info/SOURCES.txt +24 -0
- addernet-0.1.0/addernet.egg-info/dependency_links.txt +1 -0
- addernet-0.1.0/addernet.egg-info/requires.txt +1 -0
- addernet-0.1.0/addernet.egg-info/top_level.txt +1 -0
- addernet-0.1.0/pyproject.toml +22 -0
- addernet-0.1.0/setup.cfg +4 -0
- addernet-0.1.0/setup.py +2 -0
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.
|
addernet-0.1.0/PKG-INFO
ADDED
|
@@ -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
|
+
```
|
addernet-0.1.0/README.md
ADDED
|
@@ -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})")
|