shem-neural 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.
@@ -0,0 +1,37 @@
1
+ Metadata-Version: 2.4
2
+ Name: shem-neural
3
+ Version: 0.1.0
4
+ Summary: shem's neural side: an RV32IM CPU compiled into network weights, and model images
5
+ Keywords: shem,containers,riscv,neural-network,language-models
6
+ Author: João Galego
7
+ Author-email: João Galego <jgalego1990@gmail.com>
8
+ License-Expression: MIT
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
13
+ Classifier: Topic :: System :: Emulators
14
+ Requires-Dist: huggingface-hub>=2.4.0
15
+ Requires-Dist: jinja2>=3.1.6
16
+ Requires-Dist: numpy>=2
17
+ Requires-Dist: safetensors>=0.4
18
+ Requires-Dist: tokenizers>=0.23.3
19
+ Requires-Dist: torch>=2.5
20
+ Requires-Python: >=3.12
21
+ Project-URL: Homepage, https://github.com/JGalego/shem
22
+ Project-URL: Documentation, https://github.com/JGalego/shem/tree/main/docs
23
+ Project-URL: Repository, https://github.com/JGalego/shem
24
+ Description-Content-Type: text/markdown
25
+
26
+ # shem-neural
27
+
28
+ The neural vCPU for [shem](https://github.com/JGalego/shem): an RV32IM CPU compiled into the weights of a network that runs one instruction per forward pass.
29
+
30
+ ![An RV32 program runs one instruction per forward pass. On ecall, the host handles the write system call, updates the return value and program counter, then resumes the guest.](https://raw.githubusercontent.com/JGalego/shem/main/docs/assets/execution.svg)
31
+
32
+ ```sh
33
+ uv run pytest # unit tests
34
+ uv run python -m shem_neural.server --socket /tmp/shem.sock # serve vCPUs to the host
35
+ ```
36
+
37
+ See the [neural vCPU documentation](https://github.com/JGalego/shem/blob/main/docs/neural.md) for how the network works and how the host talks to it.
@@ -0,0 +1,12 @@
1
+ # shem-neural
2
+
3
+ The neural vCPU for [shem](https://github.com/JGalego/shem): an RV32IM CPU compiled into the weights of a network that runs one instruction per forward pass.
4
+
5
+ ![An RV32 program runs one instruction per forward pass. On ecall, the host handles the write system call, updates the return value and program counter, then resumes the guest.](https://raw.githubusercontent.com/JGalego/shem/main/docs/assets/execution.svg)
6
+
7
+ ```sh
8
+ uv run pytest # unit tests
9
+ uv run python -m shem_neural.server --socket /tmp/shem.sock # serve vCPUs to the host
10
+ ```
11
+
12
+ See the [neural vCPU documentation](https://github.com/JGalego/shem/blob/main/docs/neural.md) for how the network works and how the host talks to it.
@@ -0,0 +1,81 @@
1
+ [project]
2
+ name = "shem-neural"
3
+ version = "0.1.0"
4
+ description = "shem's neural side: an RV32IM CPU compiled into network weights, and model images"
5
+ readme = "README.md"
6
+ license = "MIT"
7
+ keywords = [
8
+ "shem",
9
+ "containers",
10
+ "riscv",
11
+ "neural-network",
12
+ "language-models",
13
+ ]
14
+ classifiers = [
15
+ "Development Status :: 3 - Alpha",
16
+ "License :: OSI Approved :: MIT License",
17
+ "Programming Language :: Python :: 3",
18
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
19
+ "Topic :: System :: Emulators",
20
+ ]
21
+ requires-python = ">=3.12"
22
+ dependencies = [
23
+ "huggingface-hub>=2.4.0",
24
+ "jinja2>=3.1.6",
25
+ "numpy>=2",
26
+ "safetensors>=0.4",
27
+ "tokenizers>=0.23.3",
28
+ "torch>=2.5",
29
+ ]
30
+
31
+ [[project.authors]]
32
+ name = "João Galego"
33
+ email = "jgalego1990@gmail.com"
34
+
35
+ [project.urls]
36
+ Homepage = "https://github.com/JGalego/shem"
37
+ Documentation = "https://github.com/JGalego/shem/tree/main/docs"
38
+ Repository = "https://github.com/JGalego/shem"
39
+
40
+ [project.scripts]
41
+ shem-neural-server = "shem_neural.server:main"
42
+ shem-neural-generate = "shem_neural.model.generate:main"
43
+ shem-neural-import = "shem_neural.model.importers:main"
44
+ shem-neural-export = "shem_neural.export:main"
45
+
46
+ [dependency-groups]
47
+ dev = [
48
+ "pytest>=8",
49
+ "transformers>=5.22.2",
50
+ ]
51
+
52
+ [tool.uv.sources.torch]
53
+ index = "pytorch-cpu"
54
+
55
+ [[tool.uv.index]]
56
+ name = "pytorch-cpu"
57
+ url = "https://download.pytorch.org/whl/cpu"
58
+ explicit = true
59
+
60
+ [tool.pytest.ini_options]
61
+ testpaths = ["tests"]
62
+
63
+ [tool.ruff]
64
+ line-length = 120
65
+ target-version = "py312"
66
+
67
+ [tool.ruff.lint]
68
+ select = [
69
+ "E",
70
+ "F",
71
+ "I",
72
+ "UP",
73
+ "B",
74
+ ]
75
+
76
+ [tool.ruff.lint.per-file-ignores]
77
+ "src/shem_neural/export.py" = ["E501"]
78
+
79
+ [build-system]
80
+ requires = ["uv_build>=0.10.9,<0.11.0"]
81
+ build-backend = "uv_build"
@@ -0,0 +1,67 @@
1
+ [project]
2
+ name = "shem-neural"
3
+ version = "0.1.0"
4
+ description = "shem's neural side: an RV32IM CPU compiled into network weights, and model images"
5
+ readme = "README.md"
6
+ license = "MIT"
7
+ authors = [{ name = "João Galego", email = "jgalego1990@gmail.com" }]
8
+ keywords = ["shem", "containers", "riscv", "neural-network", "language-models"]
9
+ classifiers = [
10
+ "Development Status :: 3 - Alpha",
11
+ "License :: OSI Approved :: MIT License",
12
+ "Programming Language :: Python :: 3",
13
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
14
+ "Topic :: System :: Emulators",
15
+ ]
16
+ requires-python = ">=3.12"
17
+ dependencies = [
18
+ "huggingface-hub>=2.4.0",
19
+ "jinja2>=3.1.6",
20
+ "numpy>=2",
21
+ "safetensors>=0.4",
22
+ "tokenizers>=0.23.3",
23
+ "torch>=2.5",
24
+ ]
25
+
26
+ [project.urls]
27
+ Homepage = "https://github.com/JGalego/shem"
28
+ Documentation = "https://github.com/JGalego/shem/tree/main/docs"
29
+ Repository = "https://github.com/JGalego/shem"
30
+
31
+ [project.scripts]
32
+ shem-neural-server = "shem_neural.server:main"
33
+ shem-neural-generate = "shem_neural.model.generate:main"
34
+ shem-neural-import = "shem_neural.model.importers:main"
35
+ shem-neural-export = "shem_neural.export:main"
36
+
37
+ [dependency-groups]
38
+ dev = [
39
+ "pytest>=8",
40
+ "transformers>=5.22.2",
41
+ ]
42
+
43
+ [tool.uv.sources]
44
+ torch = { index = "pytorch-cpu" }
45
+
46
+ [[tool.uv.index]]
47
+ name = "pytorch-cpu"
48
+ url = "https://download.pytorch.org/whl/cpu"
49
+ explicit = true
50
+
51
+ [build-system]
52
+ requires = ["uv_build>=0.10.9,<0.11.0"]
53
+ build-backend = "uv_build"
54
+
55
+ [tool.pytest.ini_options]
56
+ testpaths = ["tests"]
57
+
58
+ [tool.ruff]
59
+ line-length = 120
60
+ target-version = "py312"
61
+
62
+ [tool.ruff.lint]
63
+ select = ["E", "F", "I", "UP", "B"]
64
+
65
+ [tool.ruff.lint.per-file-ignores]
66
+ # The model card is Markdown, one line per paragraph
67
+ "src/shem_neural/export.py" = ["E501"]
@@ -0,0 +1,7 @@
1
+ """A neural vCPU for shem: an RV32IM CPU compiled into network weights.
2
+
3
+ `isa` describes the instruction set as data, `compile` turns that description
4
+ into the network's weight tensors, `cpu` is the network, one instruction per
5
+ forward pass, `vcpu` wraps it in the interface the shem host drives, and
6
+ `server` exposes vCPUs to the host over a Unix socket.
7
+ """
@@ -0,0 +1,117 @@
1
+ """Compile the RV32IM ISA table into lookup and routing weights.
2
+
3
+ Each tensor is derived from `isa.INSTRUCTIONS`; nothing is trained.
4
+ """
5
+
6
+ from pathlib import Path
7
+
8
+ import torch
9
+ from safetensors.torch import load_file, save_file
10
+
11
+ from . import isa
12
+
13
+ DTYPE = torch.float64
14
+
15
+
16
+ def _matcher(patterns: list[tuple[int, int]], width: int) -> tuple[torch.Tensor, torch.Tensor]:
17
+ """Weights and bias of a layer that counts mismatches: for bits `x`,
18
+ `x @ w.T + c` is how many masked bits differ from each pattern's match.
19
+ `relu(1 - that)` is then a one-hot over the patterns."""
20
+ w = torch.zeros(len(patterns), width, dtype=DTYPE)
21
+ c = torch.zeros(len(patterns), dtype=DTYPE)
22
+ for k, (mask, match) in enumerate(patterns):
23
+ for i in range(width):
24
+ if mask >> i & 1:
25
+ bit = match >> i & 1
26
+ w[k, i] = 1 - 2 * bit
27
+ c[k] += bit
28
+ return w, c
29
+
30
+
31
+ def _immediate(fmt: str) -> torch.Tensor:
32
+ """A (32, 32) selection matrix from instruction bits to immediate bits,
33
+ with sign extension: row j says which instruction bit is immediate bit j."""
34
+ source: dict[int, int] = {}
35
+ if fmt == "I":
36
+ source = {j: 20 + j for j in range(11)}
37
+ sign = 11
38
+ elif fmt == "S":
39
+ source = {j: 7 + j for j in range(5)} | {j: 20 + j for j in range(5, 11)}
40
+ sign = 11
41
+ elif fmt == "B":
42
+ source = {j: 7 + j for j in range(1, 5)} | {j: 20 + j for j in range(5, 11)} | {11: 7}
43
+ sign = 12
44
+ elif fmt == "U":
45
+ source = {j: j for j in range(12, 32)}
46
+ sign = 32
47
+ elif fmt == "J":
48
+ source = {j: 20 + j for j in range(1, 11)} | {11: 20} | {j: j for j in range(12, 20)}
49
+ sign = 20
50
+ source |= {j: 31 for j in range(sign, 32)}
51
+ select = torch.zeros(32, 32, dtype=DTYPE)
52
+ for out, bit in source.items():
53
+ select[out, bit] = 1
54
+ return select
55
+
56
+
57
+ def _route(values: list[str | None], names: tuple[str, ...]) -> torch.Tensor:
58
+ """One row per instruction, one-hot over `names` (all zeros for None)."""
59
+ route = torch.zeros(len(values), len(names), dtype=DTYPE)
60
+ for k, value in enumerate(values):
61
+ if value is not None:
62
+ route[k, names.index(value)] = 1
63
+ return route
64
+
65
+
66
+ def compile_weights() -> dict[str, torch.Tensor]:
67
+ """Build every tensor the network uses."""
68
+ ins = isa.INSTRUCTIONS
69
+ decode_w, decode_c = _matcher([(i.mask, i.match) for i in ins], 32)
70
+ index_w, index_c = _matcher([(0x1F, j) for j in range(32)], 5)
71
+
72
+ sll = torch.zeros(32, 32, 32, dtype=DTYPE) # [shift, out bit, in bit]
73
+ srl = torch.zeros(32, 32, 32, dtype=DTYPE)
74
+ sra_fill = torch.zeros(32, 32, dtype=DTYPE) # [shift, out bit]: copies of the sign bit
75
+ for s in range(32):
76
+ for i in range(32):
77
+ if i - s >= 0:
78
+ sll[s, i, i - s] = 1
79
+ if i + s < 32:
80
+ srl[s, i, i + s] = 1
81
+ else:
82
+ sra_fill[s, i] = 1
83
+
84
+ byte_bits = torch.tensor([[v >> j & 1 for j in range(8)] for v in range(256)], dtype=DTYPE)
85
+ return {
86
+ "decode_w": decode_w,
87
+ "decode_c": decode_c,
88
+ "index_w": index_w,
89
+ "index_c": index_c,
90
+ "imm_bits": torch.stack([_immediate(f) for f in isa.FORMATS]),
91
+ "format_route": _route([i.format for i in ins], isa.FORMATS),
92
+ "uses_imm": torch.tensor([float(i.uses_imm) for i in ins], dtype=DTYPE),
93
+ "result_route": _route([i.result for i in ins], isa.RESULTS),
94
+ "next_pc_route": _route([i.next_pc for i in ins], isa.NEXT_PC),
95
+ "branch_route": _route([i.branch for i in ins], isa.BRANCHES),
96
+ "exit_route": _route([i.exit for i in ins], isa.EXITS),
97
+ "load_size": torch.tensor([float(i.load) for i in ins], dtype=DTYPE),
98
+ "store_size": torch.tensor([float(i.store) for i in ins], dtype=DTYPE),
99
+ "sll": sll,
100
+ "srl": srl,
101
+ "sra_fill": sra_fill,
102
+ "byte_bits": byte_bits,
103
+ "powers": torch.tensor([2.0**i for i in range(32)], dtype=DTYPE),
104
+ }
105
+
106
+
107
+ def to(weights: dict[str, torch.Tensor], device: torch.device | str) -> dict[str, torch.Tensor]:
108
+ """The weights on `device`, where the network will run."""
109
+ return {name: tensor.to(device) for name, tensor in weights.items()}
110
+
111
+
112
+ def save(weights: dict[str, torch.Tensor], path: Path) -> None:
113
+ save_file({name: t.contiguous() for name, t in weights.items()}, str(path))
114
+
115
+
116
+ def load(path: Path) -> dict[str, torch.Tensor]:
117
+ return load_file(str(path))