|
1 | | -# AIG in Rust (last updated: 6/22/26) |
| 1 | +# raig |
2 | 2 |
|
3 | | -And-Inverter Graphs (AIGs) implemented in Rust for formal verification and circuit synthesis. |
| 3 | +`raig` (pronounced “rage”) is a dependency-free library for working with |
| 4 | +<a href="https://en.wikipedia.org/wiki/And-inverter_graph" target="_blank" rel="noopener noreferrer">And-Inverter Graphs (AIGs)</a> |
| 5 | +in Rust, developed at |
| 6 | +<a href="https://capra.cs.cornell.edu/" target="_blank" rel="noopener noreferrer">Cornell's Capra Lab</a>. |
4 | 7 |
|
5 | | -## Overview |
| 8 | +An AIG represents Boolean logic with AND gates and inverted edges. This compact |
| 9 | +form is useful for logic verification, synthesis, and testing tools. |
6 | 10 |
|
7 | | -An And-Inverter Graph (AIG) is a data structure used to represent Boolean logic circuits. Since any Boolean circuit can be represented using only `AND` and `NOT`, an AIG represents nodes as `AND` gates and edges as either regular or inverted connections |
| 11 | +The library has no default dependencies. The optional `cli` feature enables the |
| 12 | +`raig` command-line tool and its `clap` dependency. |
8 | 13 |
|
| 14 | +## Installation |
9 | 15 |
|
10 | | -## Internal Representation |
| 16 | +Add the library to a Rust project: |
11 | 17 |
|
12 | | -The graph stores AIG nodes in a `Vec<AigNode>`: |
13 | | - |
14 | | -```rust |
15 | | -pub struct AigGraph { |
16 | | - nodes: Vec<AigNode>, |
17 | | -} |
18 | | -``` |
19 | | - |
20 | | -AND nodes store two child `NodeId: u32`s: |
21 | | - |
22 | | -```rust |
23 | | -AigNode { |
24 | | - left: NodeId, |
25 | | - right: NodeId, |
26 | | -} |
27 | | -``` |
28 | | - |
29 | | -Each child `NodeId` can refer to a constant, an input, an AND node, or an inverted version of any of those. This means NOT gates are not stored as separate nodes. Instead, inversion is represented directly on the `NodeId`. |
30 | | - |
31 | | -```rust |
32 | | -pub struct AigNode { |
33 | | - left: NodeId, |
34 | | - right: NodeId, |
35 | | -} |
36 | | -``` |
37 | | - |
38 | | -The least significant bit of a `NodeId` is used as the inversion bit: |
39 | | - |
40 | | -```text |
41 | | -even NodeId = regular signal |
42 | | -odd NodeId = inverted signal |
43 | | -``` |
44 | | - |
45 | | -So inverting a `NodeId` just toggles the last bit. |
46 | | - |
47 | | -```text |
48 | | -a = NodeId(2); |
49 | | -!a = NodeId(3); |
50 | | -b = NodeId(4); |
51 | | -!b = NodeId(5); |
52 | | -``` |
53 | | - |
54 | | -Constants are represented directly as special reserved `NodeId` values: |
55 | | - |
56 | | -```rust |
57 | | -impl NodeId { |
58 | | - pub const FALSE: NodeId = NodeId(0); |
59 | | - pub const TRUE: NodeId = NodeId(1); |
60 | | -} |
61 | | -``` |
62 | | - |
63 | | -This works because `NodeId(1)` is just `NodeId(0)` with the inversion bit set: |
64 | | - |
65 | | -```text |
66 | | -NodeId(0) = false |
67 | | -NodeId(1) = !false = true |
68 | | -``` |
69 | | - |
70 | | -Constants are not stored as nodes in the graph vector. Real graph nodes start at `NodeId(2)` since `NodeId(0)` and `NodeId(1)` are reserved for the constants `true` and `false`: |
71 | | - |
72 | | -```text |
73 | | -graph[0] -> NodeId(2) |
74 | | -graph[1] -> NodeId(4) |
75 | | -graph[2] -> NodeId(6) |
76 | | -``` |
77 | | - |
78 | | -Their inverted versions are represented by setting the least significant bit: |
79 | | - |
80 | | -```text |
81 | | -NodeId(2) = graph[0] |
82 | | -NodeId(3) = !graph[0] |
83 | | -
|
84 | | -NodeId(4) = graph[1] |
85 | | -NodeId(5) = !graph[1] |
86 | | -
|
87 | | -NodeId(6) = graph[2] |
88 | | -NodeId(7) = !graph[2] |
89 | | -``` |
90 | | - |
91 | | -Inputs are stored as `NodeId`s and are represented by setting both child fields to a special marker value: |
92 | | - |
93 | | -```rust |
94 | | -const INPUT_NODE_MARKER: NodeId = NodeId(NODE_ID_MASK); |
95 | | -``` |
96 | | - |
97 | | -`NODE_ID_MASK` is all `1`s except for the least significant inversion bit: |
98 | | - |
99 | | -```text |
100 | | -NODE_ID_MASK = 11111111111111111111111111111110 |
101 | | -``` |
102 | | - |
103 | | -So the input marker is: |
104 | | - |
105 | | -```text |
106 | | -INPUT_NODE_MARKER = NodeId(11111111111111111111111111111110) |
107 | | -``` |
108 | | - |
109 | | -For example, an input node is stored like this: |
110 | | - |
111 | | -```rust |
112 | | -AigNode { |
113 | | - left: INPUT_NODE_MARKER, |
114 | | - right: INPUT_NODE_MARKER, |
115 | | -} |
116 | | -``` |
117 | | - |
118 | | -While multiple inputs contain the same internal marker data, but they are still different inputs because they have different `NodeId`s: |
119 | | - |
120 | | -```text |
121 | | -graph[0] = input node -> NodeId(2) |
122 | | -graph[1] = input node -> NodeId(4) |
123 | | -graph[2] = input node -> NodeId(6) |
124 | | -``` |
125 | | - |
126 | | -Latches are also stored as graph nodes. They are recognized by setting the left child to the same marker and storing the latch input, or next-state signal, in the right child: |
127 | | - |
128 | | -```rust |
129 | | -AigNode { |
130 | | - left: INPUT_NODE_MARKER, |
131 | | - right: next_state, |
132 | | -} |
133 | | -``` |
134 | | - |
135 | | -This lets latch state variables have stable `NodeId`s like inputs and AND nodes, while the right side points to the signal that drives the latch on the next step. |
136 | | - |
137 | | - |
138 | | - |
139 | | - |
140 | | - |
141 | | -## AIGER Input Support |
142 | | - |
143 | | -The parser supports both ASCII `.aag` files and binary `.aig` files. |
144 | | - |
145 | | -ASCII AIGER files begin with a header of the form: |
146 | | - |
147 | | -```text |
148 | | -aag M I L O A |
149 | | -``` |
150 | | - |
151 | | -Binary AIGER files use the same counts with an `aig` header: |
152 | | - |
153 | | -```text |
154 | | -aig M I L O A |
155 | | -``` |
156 | | - |
157 | | -where: |
158 | | - |
159 | | -```text |
160 | | -M = maximum variable index |
161 | | -I = number of inputs |
162 | | -L = number of latches |
163 | | -O = number of outputs |
164 | | -A = number of AND gates |
| 18 | +```sh |
| 19 | +cargo add raig |
165 | 20 | ``` |
166 | 21 |
|
167 | | -The header must satisfy: |
| 22 | +Install the command-line tool: |
168 | 23 |
|
169 | | -```text |
170 | | -M >= I + L + A |
| 24 | +```sh |
| 25 | +cargo install raig --features cli |
171 | 26 | ``` |
172 | 27 |
|
173 | | -Latch lines are supported in the ASCII parser: |
174 | | - |
175 | | -```text |
176 | | -<latch literal> <next-state literal> [reset] |
177 | | -``` |
178 | | - |
179 | | -The optional reset field is currently accepted only when it is `0`, because reset values are not represented in `AigNode`. |
180 | | - |
181 | | -In binary AIGER, input literals and latch current-state literals are implicit. The parser reads one next-state literal per latch, one output literal per output, then decodes each AND gate from its binary delta encoding. Binary files must satisfy: |
182 | | - |
183 | | -```text |
184 | | -M = I + L + A |
185 | | -``` |
186 | | - |
187 | | -The parser is split by responsibility: |
188 | | - |
189 | | -```text |
190 | | -src/aiger_parser.rs = header parsing, format validation, dispatch |
191 | | -src/aiger_ascii_parser.rs = ASCII body parsing |
192 | | -src/aiger_binary_parser.rs = binary body parsing |
193 | | -``` |
194 | | - |
195 | | -## Parsing an AIGER File |
196 | | - |
197 | | -To parse an AIGER file, use: |
198 | | - |
199 | | -```rust |
200 | | -run_parser_with_options(file_name: &str, pre_optimize: bool) -> io::Result<()> |
201 | | -``` |
202 | | - |
203 | | -Example: |
204 | | - |
205 | | -```rust |
206 | | -run_parser_with_options("example.aag", true)?; |
207 | | -run_parser_with_options("example.aig", true)?; |
208 | | -``` |
209 | | - |
210 | | -The `pre_optimize` option controls whether the parser performs simple on-the-fly optimizations while building the graph. |
211 | | - |
212 | | -If `pre_optimize` is `true`, the parser simplifies expressions before inserting new AND nodes. For example: |
213 | | - |
214 | | -```text |
215 | | -x & false = false |
216 | | -x & true = x |
217 | | -x & x = x |
218 | | -x & !x = false |
219 | | -... |
220 | | -``` |
| 28 | +## License |
221 | 29 |
|
222 | | -If `pre_optimize` is `false`, the parser builds the graph directly from the AIGER file without applying these simplifications. |
| 30 | +Licensed under the MIT license. |
0 commit comments