faultforge 0.2.1__tar.gz → 0.3.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.
Files changed (87) hide show
  1. faultforge-0.3.0/LICENSE +35 -0
  2. {faultforge-0.2.1 → faultforge-0.3.0}/PKG-INFO +46 -37
  3. {faultforge-0.2.1 → faultforge-0.3.0}/README.md +39 -35
  4. {faultforge-0.2.1 → faultforge-0.3.0}/crates/bindings/src/secded.rs +5 -1
  5. {faultforge-0.2.1 → faultforge-0.3.0}/crates/memory/src/bit_buffer/impls_numpy.rs +9 -3
  6. {faultforge-0.2.1 → faultforge-0.3.0}/crates/memory/src/sequence/non_uniform.rs +3 -1
  7. {faultforge-0.2.1 → faultforge-0.3.0}/pyproject.toml +26 -2
  8. {faultforge-0.2.1 → faultforge-0.3.0}/src/faultforge/__init__.py +11 -25
  9. {faultforge-0.2.1 → faultforge-0.3.0}/src/faultforge/_internal/dataset.py +51 -49
  10. {faultforge-0.2.1 → faultforge-0.3.0}/src/faultforge/_internal/dtype.py +20 -0
  11. {faultforge-0.2.1 → faultforge-0.3.0}/src/faultforge/_internal/experiment.py +3 -2
  12. faultforge-0.2.1/src/faultforge/_internal/common.py → faultforge-0.3.0/src/faultforge/_internal/io.py +1 -10
  13. {faultforge-0.2.1 → faultforge-0.3.0}/src/faultforge/_internal/loading/abc.py +20 -8
  14. {faultforge-0.2.1 → faultforge-0.3.0}/src/faultforge/_internal/loading/cifar.py +43 -11
  15. {faultforge-0.2.1 → faultforge-0.3.0}/src/faultforge/_internal/loading/imagenet.py +40 -9
  16. faultforge-0.3.0/src/faultforge/_internal/loading/transform.py +20 -0
  17. faultforge-0.3.0/src/faultforge/_internal/metric.py +518 -0
  18. faultforge-0.3.0/src/faultforge/_internal/progress.py +259 -0
  19. {faultforge-0.2.1 → faultforge-0.3.0}/src/faultforge/_internal/tensor.py +3 -1
  20. {faultforge-0.2.1 → faultforge-0.3.0}/src/faultforge/dataset.py +6 -0
  21. {faultforge-0.2.1 → faultforge-0.3.0}/src/faultforge/dtype.py +4 -0
  22. faultforge-0.3.0/src/faultforge/io.py +14 -0
  23. faultforge-0.3.0/src/faultforge/metric.py +42 -0
  24. {faultforge-0.2.1 → faultforge-0.3.0}/src/faultforge/progress.py +8 -0
  25. faultforge-0.2.1/src/faultforge/_internal/experiments/__init__.py +0 -0
  26. faultforge-0.2.1/src/faultforge/_internal/experiments/encoded_memory.py +0 -728
  27. faultforge-0.2.1/src/faultforge/_internal/progress.py +0 -198
  28. faultforge-0.2.1/src/faultforge/experiments/__init__.py +0 -1
  29. faultforge-0.2.1/src/faultforge/experiments/encoded_memory.py +0 -26
  30. {faultforge-0.2.1 → faultforge-0.3.0}/Cargo.lock +0 -0
  31. {faultforge-0.2.1 → faultforge-0.3.0}/Cargo.toml +0 -0
  32. {faultforge-0.2.1 → faultforge-0.3.0}/crates/bindings/Cargo.toml +0 -0
  33. {faultforge-0.2.1 → faultforge-0.3.0}/crates/bindings/src/cep.rs +0 -0
  34. {faultforge-0.2.1 → faultforge-0.3.0}/crates/bindings/src/common.rs +0 -0
  35. {faultforge-0.2.1 → faultforge-0.3.0}/crates/bindings/src/fault.rs +0 -0
  36. {faultforge-0.2.1 → faultforge-0.3.0}/crates/bindings/src/fault_injection.rs +0 -0
  37. {faultforge-0.2.1 → faultforge-0.3.0}/crates/bindings/src/lib.rs +0 -0
  38. {faultforge-0.2.1 → faultforge-0.3.0}/crates/bindings/src/mset.rs +0 -0
  39. {faultforge-0.2.1 → faultforge-0.3.0}/crates/bindings/src/picker.rs +0 -0
  40. {faultforge-0.2.1 → faultforge-0.3.0}/crates/memory/Cargo.toml +0 -0
  41. {faultforge-0.2.1 → faultforge-0.3.0}/crates/memory/benches/chunking.rs +0 -0
  42. {faultforge-0.2.1 → faultforge-0.3.0}/crates/memory/src/bit_buffer/bit.rs +0 -0
  43. {faultforge-0.2.1 → faultforge-0.3.0}/crates/memory/src/bit_buffer/impls.rs +0 -0
  44. {faultforge-0.2.1 → faultforge-0.3.0}/crates/memory/src/bit_buffer/iter.rs +0 -0
  45. {faultforge-0.2.1 → faultforge-0.3.0}/crates/memory/src/bit_buffer/result.rs +0 -0
  46. {faultforge-0.2.1 → faultforge-0.3.0}/crates/memory/src/bit_buffer/tests.rs +0 -0
  47. {faultforge-0.2.1 → faultforge-0.3.0}/crates/memory/src/bit_buffer.rs +0 -0
  48. {faultforge-0.2.1 → faultforge-0.3.0}/crates/memory/src/byte_buffer/impls.rs +0 -0
  49. {faultforge-0.2.1 → faultforge-0.3.0}/crates/memory/src/byte_buffer/tests.rs +0 -0
  50. {faultforge-0.2.1 → faultforge-0.3.0}/crates/memory/src/byte_buffer.rs +0 -0
  51. {faultforge-0.2.1 → faultforge-0.3.0}/crates/memory/src/chunks/tests.rs +0 -0
  52. {faultforge-0.2.1 → faultforge-0.3.0}/crates/memory/src/chunks.rs +0 -0
  53. {faultforge-0.2.1 → faultforge-0.3.0}/crates/memory/src/encoding/embedded_parity/tests.rs +0 -0
  54. {faultforge-0.2.1 → faultforge-0.3.0}/crates/memory/src/encoding/embedded_parity.rs +0 -0
  55. {faultforge-0.2.1 → faultforge-0.3.0}/crates/memory/src/encoding/majority/tests.rs +0 -0
  56. {faultforge-0.2.1 → faultforge-0.3.0}/crates/memory/src/encoding/majority.rs +0 -0
  57. {faultforge-0.2.1 → faultforge-0.3.0}/crates/memory/src/encoding/secded/tests.rs +0 -0
  58. {faultforge-0.2.1 → faultforge-0.3.0}/crates/memory/src/encoding/secded.rs +0 -0
  59. {faultforge-0.2.1 → faultforge-0.3.0}/crates/memory/src/encoding.rs +0 -0
  60. {faultforge-0.2.1 → faultforge-0.3.0}/crates/memory/src/fault.rs +0 -0
  61. {faultforge-0.2.1 → faultforge-0.3.0}/crates/memory/src/lib.rs +0 -0
  62. {faultforge-0.2.1 → faultforge-0.3.0}/crates/memory/src/limited.rs +0 -0
  63. {faultforge-0.2.1 → faultforge-0.3.0}/crates/memory/src/sequence/uniform.rs +0 -0
  64. {faultforge-0.2.1 → faultforge-0.3.0}/crates/memory/src/sequence.rs +0 -0
  65. {faultforge-0.2.1 → faultforge-0.3.0}/crates/picker/Cargo.toml +0 -0
  66. {faultforge-0.2.1 → faultforge-0.3.0}/crates/picker/src/lib.rs +0 -0
  67. {faultforge-0.2.1 → faultforge-0.3.0}/src/faultforge/_internal/__init__.py +0 -0
  68. {faultforge-0.2.1 → faultforge-0.3.0}/src/faultforge/_internal/encoding/__init__.py +0 -0
  69. {faultforge-0.2.1 → faultforge-0.3.0}/src/faultforge/_internal/encoding/abc.py +0 -0
  70. {faultforge-0.2.1 → faultforge-0.3.0}/src/faultforge/_internal/encoding/cep.py +0 -0
  71. {faultforge-0.2.1 → faultforge-0.3.0}/src/faultforge/_internal/encoding/identity.py +0 -0
  72. {faultforge-0.2.1 → faultforge-0.3.0}/src/faultforge/_internal/encoding/mset.py +0 -0
  73. {faultforge-0.2.1 → faultforge-0.3.0}/src/faultforge/_internal/encoding/nn.py +0 -0
  74. {faultforge-0.2.1 → faultforge-0.3.0}/src/faultforge/_internal/encoding/secded.py +0 -0
  75. {faultforge-0.2.1 → faultforge-0.3.0}/src/faultforge/_internal/encoding/sequence.py +0 -0
  76. {faultforge-0.2.1 → faultforge-0.3.0}/src/faultforge/_internal/fault.py +0 -0
  77. {faultforge-0.2.1 → faultforge-0.3.0}/src/faultforge/_internal/fingerprint.py +0 -0
  78. {faultforge-0.2.1 → faultforge-0.3.0}/src/faultforge/_internal/loading/__init__.py +0 -0
  79. {faultforge-0.2.1 → faultforge-0.3.0}/src/faultforge/_rust/__init__.pyi +0 -0
  80. {faultforge-0.2.1 → faultforge-0.3.0}/src/faultforge/_rust/cep.pyi +0 -0
  81. {faultforge-0.2.1 → faultforge-0.3.0}/src/faultforge/_rust/mset.pyi +0 -0
  82. {faultforge-0.2.1 → faultforge-0.3.0}/src/faultforge/_rust/secded.pyi +0 -0
  83. {faultforge-0.2.1 → faultforge-0.3.0}/src/faultforge/encoding.py +0 -0
  84. {faultforge-0.2.1 → faultforge-0.3.0}/src/faultforge/experiment.py +0 -0
  85. {faultforge-0.2.1 → faultforge-0.3.0}/src/faultforge/fingerprint.py +0 -0
  86. {faultforge-0.2.1 → faultforge-0.3.0}/src/faultforge/loading.py +0 -0
  87. {faultforge-0.2.1 → faultforge-0.3.0}/src/faultforge/py.typed +0 -0
@@ -0,0 +1,35 @@
1
+ Copyright (c) 2025 Marten Roots
2
+
3
+ The Universal Permissive License (UPL), Version 1.0
4
+
5
+ Subject to the condition set forth below, permission is hereby granted to any
6
+ person obtaining a copy of this software, associated documentation and/or data
7
+ (collectively the "Software"), free of charge and under any and all copyright
8
+ rights in the Software, and any and all patent rights owned or freely
9
+ licensable by each licensor hereunder covering either (i) the unmodified
10
+ Software as contributed to or provided by such licensor, or (ii) the Larger
11
+ Works (as defined below), to deal in both
12
+
13
+ (a) the Software, and
14
+ (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
15
+ one is included with the Software (each a "Larger Work" to which the Software
16
+ is contributed by such licensors),
17
+
18
+ without restriction, including without limitation the rights to copy, create
19
+ derivative works of, display, perform, and distribute the Software and make,
20
+ use, sell, offer for sale, import, export, have made, and have sold the
21
+ Software and the Larger Work(s), and to sublicense the foregoing rights on
22
+ either these or other terms.
23
+
24
+ This license is subject to the following condition:
25
+ The above copyright notice and either this complete permission notice or at
26
+ a minimum a reference to the UPL must be included in all copies or
27
+ substantial portions of the Software.
28
+
29
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
31
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
32
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
33
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
34
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
35
+ SOFTWARE.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: faultforge
3
- Version: 0.2.1
3
+ Version: 0.3.0
4
4
  Classifier: Programming Language :: Rust
5
5
  Classifier: Programming Language :: Python :: Implementation :: CPython
6
6
  Classifier: Programming Language :: Python :: Implementation :: PyPy
@@ -11,10 +11,15 @@ Requires-Dist: scipy>=1.17.1
11
11
  Requires-Dist: timm>=1.0.22
12
12
  Requires-Dist: torch>=2.7.1
13
13
  Requires-Dist: torchvision>=0.22.1
14
+ License-File: LICENSE
14
15
  Summary: A framework for reproducible hardware fault-injection experiments on PyTorch models
15
- Author-email: Marten Roots <marten.roots@gmail.com>
16
+ Author-email: Marten Roots <marten.roots@gmail.com>, Sven-Markus Loorits <sven.loorits@proton.me>
17
+ License-Expression: UPL-1.0
16
18
  Requires-Python: >=3.14
17
19
  Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
20
+ Project-URL: Changelog, https://github.com/rezzubs/faultforge/blob/main/CHANGELOG.md
21
+ Project-URL: Issues, https://github.com/rezzubs/faultforge/issues
22
+ Project-URL: Repository, https://github.com/rezzubs/faultforge
18
23
 
19
24
  # FaultForge
20
25
 
@@ -34,7 +39,9 @@ its first experiment.
34
39
  - [`Fingerprint`](docs/library.md#fingerprint)-based verification: resuming or
35
40
  comparing a saved result against a changed configuration fails loudly with
36
41
  a precise diff, instead of silently mixing incompatible data.
37
- - A composable [encoding framework](docs/experiments/encoded_memory.md#encoding-techniques)
42
+ - A generic and extensible system for [reliabilty
43
+ metrics](src/faultforge/metric.py).
44
+ - A composable [encoding framework](experiments/encoded_memory/README.md#encoding-techniques)
38
45
  (`Encoder`/`Encoding`, chainable via `EncoderSequence`) with three built-in
39
46
  ECC-style techniques - SECDED (Hamming codes), MSET, and CEP - usable
40
47
  standalone or combined.
@@ -42,7 +49,7 @@ its first experiment.
42
49
  repeat-free fault-location sampler, and a batched injection API built for
43
50
  performance.
44
51
  - One ready-made experiment today,
45
- [`encoded_memory`](docs/experiments/encoded_memory.md): fault injection into
52
+ [`encoded_memory`](experiments/encoded_memory/README.md): fault injection into
46
53
  ECC-protected model parameters, with built-in CIFAR-10/100 and ImageNet
47
54
  model/dataset loading and a CLI for recording and plotting results. It
48
55
  doubles as the reference implementation to follow when adding a new
@@ -52,58 +59,59 @@ its first experiment.
52
59
 
53
60
  ## Installation
54
61
 
55
- FaultForge is split into two packages:
62
+ ### Prebuilt packages from PyPI
56
63
 
57
- - [`faultforge`](https://pypi.org/project/faultforge/) - the library.
58
- - [`faultforge-cli`](https://pypi.org/project/faultforge-cli/) - a CLI built
59
- on top of it (installs a `faultforge` command).
64
+ Using pip:
60
65
 
61
66
  ```sh
62
- pip install faultforge # library only
63
- pip install faultforge-cli # adds the `faultforge` CLI command
67
+ pip install faultforge
64
68
  ```
65
69
 
66
- Building from source requires a Rust toolchain, since the library's
67
- performance-critical parts are a PyO3 extension compiled with
68
- [maturin](https://www.maturin.rs/). Install one with your system package
69
- manager or via [rustup](https://rustup.rs/). No toolchain is needed when
70
- installing a prebuilt wheel from PyPI.
70
+ Using uv:
71
+
72
+ ```sh
73
+ uv add faultforge
74
+ ```
75
+
76
+
77
+ [`faultforge`](https://pypi.org/project/faultforge/) is the only package
78
+ published to PyPI - it's the framework only, with no CLI and no ready-made
79
+ experiments.
71
80
 
72
81
  ### Installing from source
73
82
 
74
- Both packages live in this repository, under `packages/faultforge` and
75
- `packages/faultforge_cli`. Point pip at a subdirectory of whichever revision
76
- you want:
83
+ Building from source requires a Rust toolchain. Install one with your system
84
+ package manager or via [rustup](https://rustup.rs/). This repository's root
85
+ package is the `faultforge` library. Point pip at whichever revision you want:
77
86
 
78
87
  ```sh
79
- # latest (main is kept in sync with the newest code going forward)
80
- pip install 'faultforge @ git+https://github.com/rezzubs/faultforge.git#subdirectory=packages/faultforge'
81
88
 
82
89
  # latest release (the latest branch tracks the most recent tagged release)
83
- pip install 'faultforge @ git+https://github.com/rezzubs/faultforge.git@latest#subdirectory=packages/faultforge'
90
+ pip install 'faultforge @ git+https://github.com/rezzubs/faultforge.git@latest'
84
91
 
85
92
  # a specific released version
86
- pip install 'faultforge @ git+https://github.com/rezzubs/faultforge.git@v0.2.0#subdirectory=packages/faultforge'
93
+ pip install 'faultforge @ git+https://github.com/rezzubs/faultforge.git@v0.2.1'
87
94
 
88
95
  # a specific commit
89
- pip install 'faultforge @ git+https://github.com/rezzubs/faultforge.git@<commit-sha>#subdirectory=packages/faultforge'
90
- ```
96
+ pip install 'faultforge @ git+https://github.com/rezzubs/faultforge.git@<commit-sha>'
91
97
 
92
- Substitute `packages/faultforge_cli` for `packages/faultforge` (and
93
- `faultforge-cli` for the package name before `@`) to install the CLI the same
94
- way.
98
+ # development (main is kept in sync with the newest code, can break at any point)
99
+ pip install 'faultforge @ git+https://github.com/rezzubs/faultforge.git'
100
+ ```
95
101
 
96
- FaultForge requires Python 3.14 or newer.
102
+ Ready-made experiments (see [Experiments](#experiments) below) aren't published
103
+ to PyPI - install one the same way, pointed at its own subdirectory under
104
+ `experiments/` See each experiment's README for details.
97
105
 
98
106
  ## Quick example
99
107
 
100
108
  ```python
101
- from faultforge.encoding import SecdedEncoder
102
- from faultforge.experiment import MaxRuns
103
- from faultforge.experiments.encoded_memory import (
109
+ from encoded_memory import (
104
110
  EncodedFaultInjection,
105
111
  ReliabilityMetric,
106
112
  )
113
+ from faultforge.encoding import SecdedEncoder
114
+ from faultforge.experiment import MaxRuns
107
115
  from faultforge.loading import Cifar, CifarDataset, CifarModel
108
116
 
109
117
  bundle = Cifar(model=CifarModel.ResNet20, dataset=CifarDataset.Cifar10)
@@ -125,17 +133,18 @@ framework's pieces and how they compose.
125
133
 
126
134
  ## Experiments
127
135
 
128
- FaultForge ships one ready-made experiment today, built as the reference
129
- implementation for adding your own:
136
+ Ready-made experiments live under [`experiments/`](experiments) as their own
137
+ packages, each pinned to a specific `faultforge` version rather than tracking
138
+ `main`. There's one today, serving as the reference implementation for
139
+ adding your own:
130
140
 
131
- - [Encoded Memory](docs/experiments/encoded_memory.md) - fault injection into
141
+ - [Encoded Memory](experiments/encoded_memory/README.md) - fault injection into
132
142
  ECC-protected model parameters, covering the available encoding techniques,
133
- the `faultforge encoded-memory` CLI, and using the experiment directly as a
134
- library.
143
+ the `encoded-memory` CLI, and using the experiment directly as a library.
135
144
 
136
145
  ## Citation
137
146
 
138
- ### [Encoded Memory](docs/experiments/encoded_memory.md)
147
+ ### [Encoded Memory](experiments/encoded_memory/README.md)
139
148
 
140
149
  There are two papers related to the encoded memory experiment.
141
150
 
@@ -16,7 +16,9 @@ its first experiment.
16
16
  - [`Fingerprint`](docs/library.md#fingerprint)-based verification: resuming or
17
17
  comparing a saved result against a changed configuration fails loudly with
18
18
  a precise diff, instead of silently mixing incompatible data.
19
- - A composable [encoding framework](docs/experiments/encoded_memory.md#encoding-techniques)
19
+ - A generic and extensible system for [reliabilty
20
+ metrics](src/faultforge/metric.py).
21
+ - A composable [encoding framework](experiments/encoded_memory/README.md#encoding-techniques)
20
22
  (`Encoder`/`Encoding`, chainable via `EncoderSequence`) with three built-in
21
23
  ECC-style techniques - SECDED (Hamming codes), MSET, and CEP - usable
22
24
  standalone or combined.
@@ -24,7 +26,7 @@ its first experiment.
24
26
  repeat-free fault-location sampler, and a batched injection API built for
25
27
  performance.
26
28
  - One ready-made experiment today,
27
- [`encoded_memory`](docs/experiments/encoded_memory.md): fault injection into
29
+ [`encoded_memory`](experiments/encoded_memory/README.md): fault injection into
28
30
  ECC-protected model parameters, with built-in CIFAR-10/100 and ImageNet
29
31
  model/dataset loading and a CLI for recording and plotting results. It
30
32
  doubles as the reference implementation to follow when adding a new
@@ -34,58 +36,59 @@ its first experiment.
34
36
 
35
37
  ## Installation
36
38
 
37
- FaultForge is split into two packages:
39
+ ### Prebuilt packages from PyPI
38
40
 
39
- - [`faultforge`](https://pypi.org/project/faultforge/) - the library.
40
- - [`faultforge-cli`](https://pypi.org/project/faultforge-cli/) - a CLI built
41
- on top of it (installs a `faultforge` command).
41
+ Using pip:
42
42
 
43
43
  ```sh
44
- pip install faultforge # library only
45
- pip install faultforge-cli # adds the `faultforge` CLI command
44
+ pip install faultforge
46
45
  ```
47
46
 
48
- Building from source requires a Rust toolchain, since the library's
49
- performance-critical parts are a PyO3 extension compiled with
50
- [maturin](https://www.maturin.rs/). Install one with your system package
51
- manager or via [rustup](https://rustup.rs/). No toolchain is needed when
52
- installing a prebuilt wheel from PyPI.
47
+ Using uv:
48
+
49
+ ```sh
50
+ uv add faultforge
51
+ ```
52
+
53
+
54
+ [`faultforge`](https://pypi.org/project/faultforge/) is the only package
55
+ published to PyPI - it's the framework only, with no CLI and no ready-made
56
+ experiments.
53
57
 
54
58
  ### Installing from source
55
59
 
56
- Both packages live in this repository, under `packages/faultforge` and
57
- `packages/faultforge_cli`. Point pip at a subdirectory of whichever revision
58
- you want:
60
+ Building from source requires a Rust toolchain. Install one with your system
61
+ package manager or via [rustup](https://rustup.rs/). This repository's root
62
+ package is the `faultforge` library. Point pip at whichever revision you want:
59
63
 
60
64
  ```sh
61
- # latest (main is kept in sync with the newest code going forward)
62
- pip install 'faultforge @ git+https://github.com/rezzubs/faultforge.git#subdirectory=packages/faultforge'
63
65
 
64
66
  # latest release (the latest branch tracks the most recent tagged release)
65
- pip install 'faultforge @ git+https://github.com/rezzubs/faultforge.git@latest#subdirectory=packages/faultforge'
67
+ pip install 'faultforge @ git+https://github.com/rezzubs/faultforge.git@latest'
66
68
 
67
69
  # a specific released version
68
- pip install 'faultforge @ git+https://github.com/rezzubs/faultforge.git@v0.2.0#subdirectory=packages/faultforge'
70
+ pip install 'faultforge @ git+https://github.com/rezzubs/faultforge.git@v0.2.1'
69
71
 
70
72
  # a specific commit
71
- pip install 'faultforge @ git+https://github.com/rezzubs/faultforge.git@<commit-sha>#subdirectory=packages/faultforge'
72
- ```
73
+ pip install 'faultforge @ git+https://github.com/rezzubs/faultforge.git@<commit-sha>'
73
74
 
74
- Substitute `packages/faultforge_cli` for `packages/faultforge` (and
75
- `faultforge-cli` for the package name before `@`) to install the CLI the same
76
- way.
75
+ # development (main is kept in sync with the newest code, can break at any point)
76
+ pip install 'faultforge @ git+https://github.com/rezzubs/faultforge.git'
77
+ ```
77
78
 
78
- FaultForge requires Python 3.14 or newer.
79
+ Ready-made experiments (see [Experiments](#experiments) below) aren't published
80
+ to PyPI - install one the same way, pointed at its own subdirectory under
81
+ `experiments/` See each experiment's README for details.
79
82
 
80
83
  ## Quick example
81
84
 
82
85
  ```python
83
- from faultforge.encoding import SecdedEncoder
84
- from faultforge.experiment import MaxRuns
85
- from faultforge.experiments.encoded_memory import (
86
+ from encoded_memory import (
86
87
  EncodedFaultInjection,
87
88
  ReliabilityMetric,
88
89
  )
90
+ from faultforge.encoding import SecdedEncoder
91
+ from faultforge.experiment import MaxRuns
89
92
  from faultforge.loading import Cifar, CifarDataset, CifarModel
90
93
 
91
94
  bundle = Cifar(model=CifarModel.ResNet20, dataset=CifarDataset.Cifar10)
@@ -107,17 +110,18 @@ framework's pieces and how they compose.
107
110
 
108
111
  ## Experiments
109
112
 
110
- FaultForge ships one ready-made experiment today, built as the reference
111
- implementation for adding your own:
113
+ Ready-made experiments live under [`experiments/`](experiments) as their own
114
+ packages, each pinned to a specific `faultforge` version rather than tracking
115
+ `main`. There's one today, serving as the reference implementation for
116
+ adding your own:
112
117
 
113
- - [Encoded Memory](docs/experiments/encoded_memory.md) - fault injection into
118
+ - [Encoded Memory](experiments/encoded_memory/README.md) - fault injection into
114
119
  ECC-protected model parameters, covering the available encoding techniques,
115
- the `faultforge encoded-memory` CLI, and using the experiment directly as a
116
- library.
120
+ the `encoded-memory` CLI, and using the experiment directly as a library.
117
121
 
118
122
  ## Citation
119
123
 
120
- ### [Encoded Memory](docs/experiments/encoded_memory.md)
124
+ ### [Encoded Memory](experiments/encoded_memory/README.md)
121
125
 
122
126
  There are two papers related to the encoded memory experiment.
123
127
 
@@ -186,7 +186,11 @@ impl PyEncoding {
186
186
  }
187
187
 
188
188
  let mut encoding = self.to_rust(py)?;
189
- encoding.apply_faults(faults.into_iter().map(|(fault, target_bit)| (fault.0, target_bit)));
189
+ encoding.apply_faults(
190
+ faults
191
+ .into_iter()
192
+ .map(|(fault, target_bit)| (fault.0, target_bit)),
193
+ );
190
194
 
191
195
  *self = PyEncoding::from_rust(py, encoding, self.data_bit_count, self.item_counts.clone())?;
192
196
 
@@ -95,7 +95,9 @@ mod tests {
95
95
  fn bijective_1d() {
96
96
  let shape = &[7usize];
97
97
  let n = total_bits(shape, u8::BITS_COUNT);
98
- let outputs: HashSet<_> = (0..n).map(|i| bit_index_to_array_index::<u8>(i, shape)).collect();
98
+ let outputs: HashSet<_> = (0..n)
99
+ .map(|i| bit_index_to_array_index::<u8>(i, shape))
100
+ .collect();
99
101
  assert_eq!(outputs.len(), n);
100
102
  }
101
103
 
@@ -103,7 +105,9 @@ mod tests {
103
105
  fn bijective_2d() {
104
106
  let shape = &[3usize, 5];
105
107
  let n = total_bits(shape, u16::BITS_COUNT);
106
- let outputs: HashSet<_> = (0..n).map(|i| bit_index_to_array_index::<u16>(i, shape)).collect();
108
+ let outputs: HashSet<_> = (0..n)
109
+ .map(|i| bit_index_to_array_index::<u16>(i, shape))
110
+ .collect();
107
111
  assert_eq!(outputs.len(), n);
108
112
  }
109
113
 
@@ -111,7 +115,9 @@ mod tests {
111
115
  fn bijective_3d() {
112
116
  let shape = &[2usize, 3, 4];
113
117
  let n = total_bits(shape, u32::BITS_COUNT);
114
- let outputs: HashSet<_> = (0..n).map(|i| bit_index_to_array_index::<u32>(i, shape)).collect();
118
+ let outputs: HashSet<_> = (0..n)
119
+ .map(|i| bit_index_to_array_index::<u32>(i, shape))
120
+ .collect();
115
121
  assert_eq!(outputs.len(), n);
116
122
  }
117
123
 
@@ -107,7 +107,9 @@ where
107
107
  It: Iterator<Item = (Fault, usize)>,
108
108
  {
109
109
  let offsets = self.bit_offsets();
110
- let total_bits = *offsets.last().expect("offsets always has at least one element");
110
+ let total_bits = *offsets
111
+ .last()
112
+ .expect("offsets always has at least one element");
111
113
 
112
114
  for (fault, bit_index) in faults {
113
115
  assert!(bit_index < total_bits, "out of bounds");
@@ -1,10 +1,13 @@
1
1
  [project]
2
2
  name = "faultforge"
3
- version = "0.2.1"
3
+ version = "0.3.0"
4
4
  description = "A framework for reproducible hardware fault-injection experiments on PyTorch models"
5
5
  readme = "README.md"
6
+ license = "UPL-1.0"
7
+ license-files = ["LICENSE"]
6
8
  authors = [
7
- { name = "Marten Roots", email = "marten.roots@gmail.com" }
9
+ { name = "Marten Roots", email = "marten.roots@gmail.com" },
10
+ { name = "Sven-Markus Loorits", email = "sven.loorits@proton.me" },
8
11
  ]
9
12
  requires-python = ">=3.14"
10
13
  dependencies = [
@@ -22,9 +25,24 @@ classifiers = [
22
25
  "Programming Language :: Python :: Implementation :: PyPy",
23
26
  ]
24
27
 
28
+ [project.urls]
29
+ Repository = "https://github.com/rezzubs/faultforge"
30
+ Issues = "https://github.com/rezzubs/faultforge/issues"
31
+ Changelog = "https://github.com/rezzubs/faultforge/blob/main/CHANGELOG.md"
32
+
33
+ [tool.uv.workspace]
34
+ members = ["experiments/encoded_memory"]
35
+
36
+ [tool.uv]
37
+ default-groups = ["dev"]
38
+
25
39
  [dependency-groups]
26
40
  dev = [
41
+ "hypothesis>=6.148.7",
27
42
  "maturin>=1.14.0",
43
+ "pytest>=9.0.2",
44
+ "ruff>=0.12.4",
45
+ "ty>=0.0.49",
28
46
  ]
29
47
 
30
48
  [build-system]
@@ -36,3 +54,9 @@ features = ["pyo3/extension-module"]
36
54
  module-name = "faultforge._rust"
37
55
  manifest-path = "crates/bindings/Cargo.toml"
38
56
  python-source = "src"
57
+
58
+ [tool.ty.rules]
59
+ missing-override-decorator = "warn"
60
+
61
+ [tool.ruff]
62
+ target-version = "py314"
@@ -20,21 +20,15 @@ submodules for details.
20
20
  with its evaluation dataset.
21
21
  - `faultforge.progress`: `Progress`, for reporting on long-running operations
22
22
  (dataset loading, encoding, fault injection, ...) via periodic log messages.
23
+ - `faultforge.io`: File I/O helpers (`open_text`, `is_compressed`) for
24
+ reading/writing save files transparently through zstd compression.
23
25
 
24
26
  To add a new kind of experiment, subclass `Experiment` and reuse
25
- `faultforge.loading`/`faultforge.dataset` for model and data handling.
26
-
27
- **Ready-made experiments**
28
-
29
- `faultforge.experiments` holds experiments built on the framework above.
30
- Currently there is one, `faultforge.experiments.encoded_memory`: encoded-
31
- memory fault injection, which measures model reliability under simulated
32
- single-event upsets in error-corrected parameter memory. It's a complete
33
- example to follow when adding a new experiment. It's built on:
34
-
35
- - `faultforge.encoding`: `Encoder`/`Encoding` pairs that transform tensors into
36
- a protected representation and back, plus `EncodedModule` for wrapping a
37
- `torch.nn.Module` so its parameters live in simulated encoded memory.
27
+ `faultforge.loading`/`faultforge.dataset` for model and data handling. This
28
+ library only holds the reusable framework - ready-made experiments (e.g.
29
+ `encoded_memory`, built on `faultforge.encoding`) live in the repository's
30
+ top-level `experiments/` directory as their own packages, so pulling in
31
+ `faultforge` never drags in an experiment's own dependencies.
38
32
 
39
33
  **Fault injection primitives**
40
34
 
@@ -49,20 +43,16 @@ root rather than in their own submodule:
49
43
  `Encoding.apply_faults`/`EncodedModule.apply_faults`) over injecting one fault
50
44
  at a time in a loop: applying a batch pays its conversion overhead once for the
51
45
  whole batch rather than once per fault.
46
+ - `bitwise_xor`: elementwise bitwise xor of two same-shape, same-dtype tensors
47
+ (floats bitcast to same-width integers first).
52
48
  """
53
49
 
54
50
  import sys
55
51
 
56
- from faultforge._internal.common import (
57
- DEFAULT_BATCH_SIZE,
58
- DEFAULT_DEVICE,
59
- AnyPath,
60
- DeviceLike,
61
- is_compressed,
62
- )
63
52
  from faultforge._internal.fault import BitFlip, Fault, StuckAt
64
53
  from faultforge._internal.fingerprint import Fingerprint
65
54
  from faultforge._internal.tensor import (
55
+ bitwise_xor,
66
56
  tensor_list_dtype,
67
57
  tensor_list_fault,
68
58
  tensor_list_faults,
@@ -72,16 +62,12 @@ from faultforge._rust import Picker
72
62
  from . import _rust
73
63
 
74
64
  __all__ = [
75
- "AnyPath",
76
65
  "BitFlip",
77
- "DEFAULT_BATCH_SIZE",
78
- "DEFAULT_DEVICE",
79
- "DeviceLike",
80
66
  "Fault",
81
67
  "Fingerprint",
82
68
  "Picker",
83
69
  "StuckAt",
84
- "is_compressed",
70
+ "bitwise_xor",
85
71
  "tensor_list_dtype",
86
72
  "tensor_list_fault",
87
73
  "tensor_list_faults",