Skip to content

Commit 2d05287

Browse files
authored
Merge pull request #23 from cucapra/crate-publish
Crate publish
2 parents 2774fd5 + cca7bb4 commit 2d05287

13 files changed

Lines changed: 450 additions & 321 deletions

File tree

.github/workflows/diff-test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ jobs:
2626
run: uv tool install turnt
2727

2828
- name: Build Rust implementation
29-
run: cargo build
29+
run: cargo build --features cli --bin raig
3030

3131
- name: Install C AIGER
3232
run: |

.github/workflows/snapshot-test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ jobs:
3838
run: cargo test --all
3939

4040
- name: Build debug binary
41-
run: cargo build
41+
run: cargo build --features cli --bin raig
4242

4343
- name: Run AIGER snapshot tests
4444
run: turnt -e raw -e opt tests/inputs/*.aag

Cargo.lock

Lines changed: 7 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,30 @@
11
[package]
2-
name = "aig"
2+
name = "raig"
33
version = "0.1.0"
4+
authors = ["Modi Goldstein-Rosenfeld <mbg237@cornell.edu>", "Adrian Sampson <asampson@cs.cornell.edu>", "Kevin Laeufer <laeufer@cornell.edu>"]
45
edition = "2024"
6+
rust-version = "1.85"
7+
description = "A library for boolean logic verification using And-Inverter Graphs (AIGs)"
8+
documentation = "https://docs.rs/raig"
9+
repository = "https://github.com/cucapra/aig"
10+
license = "MIT"
11+
keywords = ["aig", "aiger", "logic", "verification", "boolean"]
12+
categories = ["data-structures", "development-tools::testing", "parsing", "simulation"]
13+
include = [
14+
"/Cargo.toml",
15+
"/LICENSE",
16+
"/README.md",
17+
"/src/**",
18+
]
519

6-
[dependencies]
7-
clap = { version = "4.6.1", features = ["derive"] }
20+
[features]
21+
default = []
22+
cli = ["dep:clap"]
23+
24+
[[bin]]
25+
name = "raig"
26+
path = "src/main.rs"
27+
required-features = ["cli"]
828

29+
[dependencies]
30+
clap = { version = "4.6.1", features = ["derive"], optional = true }

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2026 Modi Goldstein-Rosenfeld, Adrian Sampson, and Kevin Laeufer
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.

README.md

Lines changed: 18 additions & 210 deletions
Original file line numberDiff line numberDiff line change
@@ -1,222 +1,30 @@
1-
# AIG in Rust (last updated: 6/22/26)
1+
# raig
22

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>.
47

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.
610

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.
813

14+
## Installation
915

10-
## Internal Representation
16+
Add the library to a Rust project:
1117

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
16520
```
16621

167-
The header must satisfy:
22+
Install the command-line tool:
16823

169-
```text
170-
M >= I + L + A
24+
```sh
25+
cargo install raig --features cli
17126
```
17227

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
22129

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

Comments
 (0)