├── .git-blame-ignore-revs ├── .github └── workflows │ └── rust.yml ├── .gitignore ├── BYODS.MD ├── CHANGELOG.MD ├── Cargo.toml ├── LICENSE ├── MACROS.MD ├── README.MD ├── ascent ├── Cargo.toml ├── examples │ ├── ascent_agg_clause.rs │ ├── ascent_disjunction_clause.rs │ ├── ascent_for_in_clause.rs │ ├── ascent_generic_program.rs │ ├── ascent_if_clause.rs │ ├── ascent_if_let_clause.rs │ ├── ascent_lattice.rs │ ├── ascent_let_clause.rs │ ├── ascent_macros_rule.rs │ ├── ascent_negation_clause.rs │ ├── ascent_source.rs │ ├── context_sensitive_flow_graph.rs │ ├── context_sensitive_flow_graph_with_records.rs │ ├── def_use_chains.rs │ ├── fibonacci.rs │ ├── fizz_buzz.rs │ ├── lists_using_recursive_enums.rs │ ├── transitive_graph_closure.rs │ └── var_points_to.rs └── src │ ├── aggregators.rs │ ├── c_lat_index.rs │ ├── c_rel_full_index.rs │ ├── c_rel_index.rs │ ├── c_rel_index_combined.rs │ ├── c_rel_index_read.rs │ ├── c_rel_no_index.rs │ ├── convert.rs │ ├── exps.rs │ ├── internal.rs │ ├── lib.rs │ ├── rel.rs │ ├── rel_index_boilerplate.rs │ ├── rel_index_read.rs │ ├── to_rel_index.rs │ └── tuple_of_borrowed.rs ├── ascent_base ├── Cargo.toml └── src │ ├── lattice.rs │ ├── lattice │ ├── bounded_set.rs │ ├── constant_propagation.rs │ ├── dual.rs │ ├── ord_lattice.rs │ ├── product.rs │ ├── set.rs │ └── tuple.rs │ ├── lib.rs │ └── util.rs ├── ascent_macro ├── Cargo.toml ├── rust-toolchain └── src │ ├── ascent_codegen.rs │ ├── ascent_hir.rs │ ├── ascent_mir.rs │ ├── ascent_syntax.rs │ ├── lib.rs │ ├── scratchpad.rs │ ├── scratchpad_template.rs │ ├── syn_utils.rs │ ├── test_errors.rs │ ├── tests.rs │ └── utils.rs ├── ascent_tests ├── Cargo.toml ├── benches │ └── benches.rs ├── rust-toolchain └── src │ ├── agg_tests.rs │ ├── analysis_exp.rs │ ├── ascent_maybe_par.rs │ ├── bin │ └── tc.rs │ ├── example_tests.rs │ ├── exps.rs │ ├── include_source_tests.rs │ ├── lib.rs │ ├── macros_tests.rs │ ├── se.rs │ ├── tests.rs │ └── utils.rs ├── byods ├── README.md └── ascent-byods-rels │ ├── Cargo.toml │ ├── examples │ ├── Cargo.toml │ ├── bench_trrel.rs │ ├── bench_trrel_uf.rs │ ├── blockchain.rs │ ├── steensgaard │ │ ├── main.rs │ │ └── openjdk_javalang_steensgaard │ │ │ ├── alloc.facts │ │ │ ├── assign.facts │ │ │ ├── load.facts │ │ │ └── store.facts │ └── tracking_alloc.rs │ └── src │ ├── adaptor │ ├── bin_rel.rs │ ├── bin_rel_plus_ternary_provider.rs │ ├── bin_rel_provider.rs │ ├── bin_rel_to_ternary.rs │ └── mod.rs │ ├── binary_rel.rs │ ├── ceqrel_ind.rs │ ├── eqrel.rs │ ├── eqrel_binary.rs │ ├── eqrel_ind.rs │ ├── eqrel_ternary.rs │ ├── fake_vec.rs │ ├── iterator_from_dyn.rs │ ├── lib.rs │ ├── rel_boilerplate.rs │ ├── test.rs │ ├── trrel.rs │ ├── trrel_binary.rs │ ├── trrel_binary_ind.rs │ ├── trrel_ternary_ind.rs │ ├── trrel_uf.rs │ ├── trrel_union_find.rs │ ├── trrel_union_find_binary_ind.rs │ ├── uf.rs │ ├── union_find.rs │ └── utils.rs ├── rustfmt.toml └── wasm-tests ├── .gitignore ├── Cargo.toml ├── README.md ├── src ├── lib.rs └── utils.rs └── tests └── web.rs /.git-blame-ignore-revs: -------------------------------------------------------------------------------- 1 | # guide: https://gist.github.com/kateinoigakukun/b0bc920e587851bfffa98b9e279175f2 2 | # Run this command for this to take effect: 3 | # $ git config blame.ignoreRevsFile .git-blame-ignore-revs 4 | 5 | # Fmt (#54) 6 | d8db7dc7eb9760b2e81d0168cfa931549ed28085 -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | schedule: 5 | - cron: '0 22 * * FRI' 6 | push: 7 | branches: [ master, ci, par, byods ] 8 | pull_request: 9 | branches: [ master, byods ] 10 | paths-ignore: 11 | - '**/*.MD' 12 | 13 | env: 14 | CARGO_TERM_COLOR: always 15 | 16 | jobs: 17 | build-test: 18 | 19 | runs-on: ubuntu-latest 20 | 21 | steps: 22 | - uses: actions/checkout@v2 23 | - run: cargo --version 24 | 25 | - name: Install Rust 26 | uses: actions-rs/toolchain@v1 27 | with: 28 | override: true 29 | toolchain: 1.73 30 | 31 | - run: cargo --version 32 | 33 | - name: Run ascent_tests tests 34 | working-directory: ./ascent_tests 35 | run: cargo test 36 | 37 | - name: Run ascent_tests tests with `--features par` 38 | working-directory: ./ascent_tests 39 | run: cargo test --features par 40 | 41 | - name: Run ./ascent tests 42 | working-directory: ./ascent 43 | run: cargo test 44 | 45 | - name: Run ascent_base tests 46 | working-directory: ./ascent_base 47 | run: cargo test 48 | 49 | - uses: jetli/wasm-pack-action@v0.3.0 50 | with: 51 | # Optional version of wasm-pack to install(eg. 'v0.9.1', 'latest') 52 | version: 'latest' 53 | 54 | - name: Run wasm tests 55 | working-directory: ./wasm-tests 56 | run: | 57 | cargo tree 58 | wasm-pack test --headless --firefox 59 | 60 | # macro tests should be last, because they produce scratchpad.rs, which my not compile 61 | - name: Run macro tests 62 | working-directory: ./ascent_macro 63 | run: cargo test test 64 | 65 | bench: 66 | runs-on: ubuntu-latest 67 | 68 | steps: 69 | - uses: actions/checkout@v2 70 | - run: cargo --version 71 | 72 | - name: Install Rust 73 | uses: actions-rs/toolchain@v1 74 | with: 75 | override: true 76 | toolchain: 1.73 77 | 78 | - name: Run Benchmarks 79 | working-directory: ./ascent_tests 80 | run: cargo bench bench_tc -- --nocapture 81 | 82 | - name: Run ascent_tests/src/bin benchmarks 83 | working-directory: ./ascent_tests 84 | run: cargo run --release --bin tc && cargo run --release --features par --bin tc 85 | 86 | examples: 87 | runs-on: ubuntu-latest 88 | 89 | steps: 90 | - uses: actions/checkout@v2 91 | 92 | - name: Install Rust 93 | uses: actions-rs/toolchain@v1 94 | with: 95 | toolchain: stable 96 | 97 | - name: run examples 98 | working-directory: ./ascent 99 | run: | 100 | for example in ./examples/*rs 101 | do 102 | cargo run --example "$(basename "${example%.rs}")" || exit 1 103 | done 104 | 105 | byods-rels: 106 | runs-on: ubuntu-latest 107 | 108 | steps: 109 | - uses: actions/checkout@v2 110 | 111 | - name: Install Rust 112 | uses: actions-rs/toolchain@v1 113 | with: 114 | toolchain: stable 115 | 116 | - name: cargo check (no-default-features) 117 | working-directory: ./byods/ascent-byods-rels 118 | run: cargo check --no-default-features 119 | 120 | - name: cargo check 121 | working-directory: ./byods/ascent-byods-rels 122 | run: cargo check 123 | 124 | - name: cargo check (examples) 125 | working-directory: ./byods/ascent-byods-rels/examples 126 | run: cargo check --bins 127 | 128 | - name: cargo test 129 | working-directory: ./byods/ascent-byods-rels 130 | run: cargo test 131 | 132 | dependency-check: 133 | runs-on: ubuntu-latest 134 | 135 | steps: 136 | - uses: actions/checkout@v2 137 | - run: cargo --version 138 | 139 | - name: Install Rust 140 | uses: actions-rs/toolchain@v1 141 | with: 142 | override: true 143 | toolchain: nightly 144 | 145 | - uses: taiki-e/install-action@cargo-hack 146 | - uses: taiki-e/install-action@cargo-minimal-versions 147 | 148 | - name: cargo minimal-versions 149 | run: cargo +stable minimal-versions check --workspace --all-features --ignore-private -v 150 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | cargo.lock 3 | .vscode 4 | **/scratchpad.rs 5 | 6 | ez3/ 7 | ez3_base/ 8 | ez3_tests/ 9 | .z3-trace 10 | nll-facts/ -------------------------------------------------------------------------------- /BYODS.MD: -------------------------------------------------------------------------------- 1 | BYODS is an extension of Ascent that allows custom data structures backing relations. The OOPSLA paper [Bring Your Own Data Structures to Datalog](https://dl.acm.org/doi/pdf/10.1145/3622840) describes the semantics and high-level design of BYODS, and presents a number of experiments demonstrating the capabilities of BYODS. 2 | 3 | With BYODS, any relation in an Ascent program can be tagged with a data structure provider: 4 | 5 | ```Rust 6 | ascent! { 7 | #[ds(ascent_byods_rels::eqrel)] 8 | relation rel(u32, u32); 9 | // ... 10 | } 11 | ``` 12 | 13 | In the above example, relation `rel` is backed by the `eqrel` data structure provider, which makes `rel` an equivalence relation. 14 | 15 | In the above example, `ascent_byods_rels::eqrel` is the path of the module containing the macros required of a data structure provider. You can define your own data structure providers. To see what macros are required of a data structure provider, see `ascent::rel`, which is the default data structure provider. 16 | 17 | The most important macros of a data structure privider are `rel_ind` and `rel_ind_common`. 18 | 19 | Macro invocations for `rel_ind_common` and other macros look like this: 20 | 21 | ```Rust 22 | my_provider::rel_ind_common!( 23 | rel, // rel name 24 | (u32, u32), // column types 25 | [[1]], // logical indices 26 | ser, // parallel (par) or serial (ser) 27 | (), // user-specified params 28 | ) 29 | ``` 30 | 31 | These macro invocations evaluate to types that implement certain traits. `rel_ind!` returned types must implement `ToRelIndex`. This trait provides an indirection, allowing relation indices to share data (the type returned by `rel_ind_common!`). The type returned by the `to_rel_index` function of this trait is used to read the relation data, and must implement `RelIndexRead` and `RelIndexReadAll` traits. The type returned by `to_rel_index_write` is used to write new values to an index, and must implement `RelIndexWrite` and `RelIndexMerge`. 32 | 33 | These traits have `Key` and `Value` associated types. `Key` is the tuple of the indexed-on columns, and `Value` is the tuple of the remaining columns. For example, for an index [1, 2] of a relation `relation rel(Col0, Col1, Col2)`, the `Key` type would be `(Col1, Col2)` and the `Value` type would be `(Col0,)`. For `RelIndexReadAll`, the `Key` and `Value` types are references to tuples, or tuples of references (see `TupleOfBorrowed`). The same is true for the `Value` type of `RelIndexRead`. 34 | 35 | `rel_ind_common!` provides sharing of data between indices of a relation. Types returned by this macro invocation only need to implement `RelIndexMerge`. 36 | 37 | 38 | To support parallel Ascent, relation indices need to implement parallel versions of the above traits. For example, `CRelIndexRead`, `CRelIndexReadAll`, and `CRelIndexWrite`. 39 | 40 | **TODO**: finish the guide -------------------------------------------------------------------------------- /CHANGELOG.MD: -------------------------------------------------------------------------------- 1 | ## 0.8.0 2 | 3 | - Composition feature: added the `ascent_source!` macro and the `include_source!` syntax (thanks to @JoranHonig 4 | for the suggestion (#44) and @StarGazerM for the idea). 5 | 6 | - Made index fields in generated Ascent code private (#58). 7 | 8 | - Added `|` as the disjunction token in disjunction clauses, deprecating `||`. 9 | 10 | - Added a small optimization where rule evaluation is skipped if any body relation is empty (#57) 11 | 12 | ## 0.7.0 13 | 14 | - Fixed unsoundness in `util::update` (#41) 15 | 16 | - Moved `ascent_par!` and required dependencies behind Cargo feature `par` (enabled by default) 17 | 18 | ## 0.6.0 19 | 20 | - BYODS (Bring Your Own Data Structures to Datalog): this version allows relations in Ascent programs to 21 | be backed by custom data structures (for improved algorithmic efficiency, modified semantics, etc.). 22 | - Introduction of the crate [`ascent-byods-rels`](https://crates.io/crates/ascent-byods-rels), which contains a number of such data structures (for equivalence and transitive relations). 23 | 24 | - Introduction of `impl` directives with different bounds from `struct` declarations for generic parameters (#25) 25 | 26 | ## 0.5.0 27 | 28 | - Parallel evaluation: with introduction of `ascent_par!` and `ascent_run_par!` macros, Ascent programs can utilize multiple CPU cores to speed up evaluation. 29 | 30 | - Made improvements to handling of rules that start with `if`, `let`, or `for` (they can be simple joins). 31 | 32 | ## 0.4.0 33 | - Reduced the size of generated code for a rule with N dynamic relations from O(N * 2^N) to O(N^2); 34 | dramatically improving compile times for some programs. 35 | 36 | ## 0.3.0 37 | - Introduced macro-in-macros! 38 | 39 | ## 0.2.1 40 | - Bug fix 41 | 42 | ## 0.2.0 43 | - Added run_timeout 44 | - Disallowed shadowed variables -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["byods/ascent-byods-rels", "ascent", "ascent_base", "ascent_macro"] 3 | exclude = ["ascent_tests", "wasm-tests"] 4 | resolver = "2" 5 | 6 | [workspace.package] 7 | version = "0.8.0" 8 | 9 | [workspace.dependencies] 10 | ascent = { path = "./ascent", default-features = false, version = "0.8.0" } 11 | ascent_macro = { path = "./ascent_macro", version = "0.8.0" } 12 | ascent_base = { path = "./ascent_base", version = "0.8.0" } 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) Individual contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /MACROS.MD: -------------------------------------------------------------------------------- 1 | # macros! 2 | 3 | Macros can be defined inside Ascent programs to avoid having to repeat code. 4 | These macros can expand to sequences of body or head clauses in Ascent rules. 5 | 6 | The syntax resembles (unstable) Rust macros 2.0. 7 | 8 | Example: Finding out what languages can be compiled to what languages *competently*. 9 | ```Rust 10 | type Lang = &'static str; 11 | type Compiler = &'static str; 12 | ascent! { 13 | relation compiler(Compiler, Lang, Lang); 14 | relation bad_compiler(Compiler); 15 | 16 | relation can_compile_to(Lang, Lang); 17 | 18 | macro compiler($from: expr, $to: expr) { 19 | compiler(name, $from, $to), !bad_compiler(name) 20 | } 21 | 22 | can_compile_to(a, b) <-- compiler!(a, b); 23 | can_compile_to(a, c) <-- compiler!(a, b), can_compile_to(b, c); 24 | } 25 | ``` 26 | 27 | Identifiers bound in macro bodies, like `name` in the example above, remain private to the macro invocation. For example, to find out what languages can be compiled in two steps, we can add the following: 28 | ```Rust 29 | ascent! { 30 | //... 31 | relation compiles_in_two_steps(Lang, Lang); 32 | compiles_in_two_steps(a, c) <-- compiler!(a, b), compiler!(b, c); 33 | } 34 | ``` 35 | And it won't require the `a -> b` compiler to be the same as the `b -> c` compiler! -------------------------------------------------------------------------------- /ascent/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ascent" 3 | version.workspace = true 4 | edition = "2021" 5 | rust-version = "1.73" # the CI (../.github/workflows/rust.yml) toolchain version must match this 6 | authors = ["Arash Sahebolamri"] 7 | description = "Logic programming in Rust" 8 | keywords = ["logic-programming", "program-analysis", "Datalog", "procedural-macro"] 9 | categories = ["compilers"] 10 | homepage = "https://s-arash.github.io/ascent/" 11 | repository = "https://github.com/s-arash/ascent" 12 | license = "MIT" 13 | readme = "../README.MD" 14 | 15 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 16 | 17 | [dependencies] 18 | ascent_macro = { workspace = true } 19 | ascent_base = { workspace = true } 20 | cfg-if = "1.0" 21 | hashbrown = {version = "0.14", features = ["raw"]} 22 | rustc-hash = "2.0" 23 | instant = "0.1" 24 | dashmap = { version = "5.5", features = ["raw-api", "rayon"], optional = true } 25 | rayon = { version = "1.5", optional = true } 26 | boxcar = "0.1.0" 27 | once_cell = { version = "1.13.1", optional = true } 28 | paste = "1.0" 29 | 30 | [dev-dependencies] 31 | 32 | [features] 33 | default = ["par"] 34 | wasm-bindgen = ["instant/wasm-bindgen"] 35 | par = ["dashmap", "hashbrown/rayon", "once_cell", "rayon"] -------------------------------------------------------------------------------- /ascent/examples/ascent_agg_clause.rs: -------------------------------------------------------------------------------- 1 | //! Aggregate `agg` clause 2 | 3 | use ascent::aggregators::{count, max, mean, min, sum}; 4 | use ascent::ascent; 5 | 6 | ascent! { 7 | // Facts: 8 | 9 | relation number(i32); 10 | 11 | // Rules: 12 | 13 | relation lowest(i32); 14 | 15 | lowest(y) <-- agg y = min(x) in number(x); 16 | 17 | relation greatest(i32); 18 | 19 | greatest(y) <-- agg y = max(x) in number(x); 20 | 21 | relation average(i32); 22 | 23 | average(y.round() as i32) <-- agg y = mean(x) in number(x); 24 | 25 | relation total(i32); 26 | 27 | total(y) <-- agg y = sum(x) in number(x); 28 | 29 | relation cardinality(usize); 30 | 31 | cardinality(y) <-- agg y = count() in number(_); 32 | } 33 | 34 | fn main() { 35 | let mut prog = AscentProgram::default(); 36 | 37 | prog.number = (1..=5).map(|n| (n,)).collect(); 38 | 39 | prog.run(); 40 | 41 | let AscentProgram { lowest, greatest, average, total, cardinality, .. } = prog; 42 | 43 | assert_eq!(lowest, vec![(1,)]); 44 | assert_eq!(greatest, vec![(5,)]); 45 | assert_eq!(average, vec![(3,)]); 46 | assert_eq!(total, vec![(15,)]); 47 | assert_eq!(cardinality, vec![(5,)]); 48 | } 49 | -------------------------------------------------------------------------------- /ascent/examples/ascent_disjunction_clause.rs: -------------------------------------------------------------------------------- 1 | //! Disjunction clause 2 | 3 | use ascent::ascent; 4 | 5 | ascent! { 6 | // Facts: 7 | 8 | relation number(i32); 9 | 10 | // Rules: 11 | 12 | relation square(i32); 13 | 14 | square(y * y) <-- number(y), number(y * y); 15 | 16 | relation even(i32); 17 | 18 | even(x) <-- number(x) if x % 2 == 0; 19 | 20 | relation even_or_square(i32); 21 | 22 | even_or_square(x) <-- (even(x) | square(x)); 23 | } 24 | 25 | fn main() { 26 | let mut prog = AscentProgram::default(); 27 | 28 | prog.number = (1..=10).map(|n| (n,)).collect(); 29 | 30 | prog.run(); 31 | 32 | let AscentProgram { mut even_or_square, .. } = prog; 33 | 34 | even_or_square.sort_by_key(|(key,)| *key); 35 | 36 | assert_eq!(even_or_square, vec![(1,), (2,), (4,), (6,), (8,), (9,), (10,),]); 37 | } 38 | -------------------------------------------------------------------------------- /ascent/examples/ascent_for_in_clause.rs: -------------------------------------------------------------------------------- 1 | //! Generative `for … in` clause 2 | 3 | use ascent::ascent; 4 | 5 | ascent! { 6 | // Facts: 7 | 8 | relation seed(i32); 9 | 10 | // Rules: 11 | 12 | relation number(i32); 13 | 14 | number(x + y) <-- seed(x), for y in 0..3; 15 | } 16 | 17 | fn main() { 18 | let mut prog = AscentProgram::default(); 19 | 20 | prog.seed = vec![(0,), (10,)]; 21 | 22 | prog.run(); 23 | 24 | let AscentProgram { mut number, .. } = prog; 25 | 26 | number.sort_by_key(|(key,)| *key); 27 | 28 | assert_eq!(number, vec![(0,), (1,), (2,), (10,), (11,), (12,),]); 29 | } 30 | -------------------------------------------------------------------------------- /ascent/examples/ascent_generic_program.rs: -------------------------------------------------------------------------------- 1 | //! Generic program 2 | //! 3 | //! The following example encodes reachability of a directed graph that is generic over its nodes. 4 | 5 | use std::hash::Hash; 6 | 7 | use ascent::ascent; 8 | 9 | #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] 10 | pub struct Node(&'static str); 11 | 12 | ascent! { 13 | struct AscentProgram; 14 | 15 | // Or alternatively (and with the same semantics!): 16 | // struct AscentProgram where N: Clone + Eq + Hash; 17 | 18 | // If you want to keep the trait bounds out of the program's 19 | // type signature and limit them to its `impl` blocks, 20 | // you could alternatively specify the program's type like this: 21 | // 22 | // struct AscentProgram; 23 | // impl AscentProgram; 24 | // 25 | // Or alternatively (and with the same semantics!): 26 | // 27 | // struct AscentProgram; 28 | // impl AscentProgram where N: Clone + Eq + Hash; 29 | 30 | // Where desirable you could even do a mix of both: 31 | // 32 | // struct AscentProgram; 33 | // impl AscentProgram; 34 | 35 | // Facts: 36 | 37 | relation node(N); 38 | relation edge(N, N); 39 | 40 | // Rules: 41 | 42 | relation reachable(N, N); 43 | 44 | reachable(x, y) <-- edge(x, y); 45 | reachable(x, z) <-- reachable(x, y), edge(y, z); 46 | } 47 | 48 | fn main() { 49 | let mut prog: AscentProgram = AscentProgram::default(); 50 | 51 | prog.node = vec![(Node("A"),), (Node("B"),), (Node("C"),)]; 52 | 53 | prog.edge = vec![(Node("A"), Node("B")), (Node("B"), Node("C"))]; 54 | 55 | prog.run(); 56 | 57 | let AscentProgram { mut reachable, .. } = prog; 58 | 59 | reachable.sort_by_key(|(_, key)| key.0); 60 | reachable.sort_by_key(|(key, _)| key.0); 61 | 62 | assert_eq!(reachable, vec![(Node("A"), Node("B")), (Node("A"), Node("C")), (Node("B"), Node("C")),]); 63 | } 64 | -------------------------------------------------------------------------------- /ascent/examples/ascent_if_clause.rs: -------------------------------------------------------------------------------- 1 | //! Conditional `if` clause 2 | 3 | use ascent::ascent; 4 | 5 | ascent! { 6 | // Facts: 7 | 8 | relation number(isize); 9 | 10 | // Rules: 11 | 12 | relation even(isize); 13 | 14 | even(x) <-- number(x), if x % 2 == 0; 15 | 16 | relation odd(isize); 17 | 18 | odd(x) <-- number(x), if x % 2 != 0; 19 | } 20 | 21 | fn main() { 22 | let mut prog = AscentProgram::default(); 23 | 24 | prog.number = (1..=5).map(|n| (n,)).collect(); 25 | 26 | prog.run(); 27 | 28 | let AscentProgram { mut even, mut odd, .. } = prog; 29 | 30 | even.sort_by_key(|(key,)| *key); 31 | 32 | assert_eq!(even, vec![(2,), (4,),]); 33 | 34 | odd.sort_by_key(|(key,)| *key); 35 | 36 | assert_eq!(odd, vec![(1,), (3,), (5,),]); 37 | } 38 | -------------------------------------------------------------------------------- /ascent/examples/ascent_if_let_clause.rs: -------------------------------------------------------------------------------- 1 | //! Conditional `if let` clause 2 | 3 | use ascent::ascent; 4 | 5 | ascent! { 6 | // Facts: 7 | 8 | relation option(Option); 9 | 10 | // Rules: 11 | 12 | relation some(isize); 13 | 14 | some(y) <-- option(x), if let Some(y) = x; 15 | 16 | // The rule above could alternatively also be written 17 | // using the following short-hand syntax: 18 | some(y) <-- option(?Some(y)); 19 | } 20 | 21 | fn main() { 22 | let mut prog = AscentProgram::default(); 23 | 24 | prog.option = vec![(None,), (Some(1),), (Some(2),), (Some(3),)]; 25 | 26 | prog.run(); 27 | 28 | let AscentProgram { mut some, .. } = prog; 29 | 30 | some.sort_by_key(|(key,)| *key); 31 | 32 | assert_eq!(some, vec![(1,), (2,), (3,),]); 33 | } 34 | -------------------------------------------------------------------------------- /ascent/examples/ascent_lattice.rs: -------------------------------------------------------------------------------- 1 | //! Aggregate `agg` clause 2 | 3 | use ascent::{Dual, ascent}; 4 | 5 | pub type Node = &'static str; 6 | 7 | ascent! { 8 | // Facts: 9 | 10 | relation edge(Node, Node, u32); 11 | 12 | // Rules: 13 | 14 | lattice shortest_path(Node, Node, Dual); 15 | 16 | shortest_path(x, y, Dual(*w)) <-- edge(x, y, w); 17 | 18 | shortest_path(x, z, Dual(w + l)) <-- 19 | edge(x, y, w), 20 | shortest_path(y, z, ?Dual(l)); 21 | } 22 | 23 | fn main() { 24 | let mut prog = AscentProgram::default(); 25 | 26 | prog.edge = vec![("A", "B", 1), ("A", "D", 4), ("B", "C", 1), ("B", "D", 1), ("C", "D", 2)]; 27 | 28 | prog.run(); 29 | 30 | let AscentProgram { mut shortest_path, .. } = prog; 31 | 32 | shortest_path.sort_by_key(|(_, key, _)| *key); 33 | shortest_path.sort_by_key(|(key, _, _)| *key); 34 | 35 | assert_eq!(shortest_path, vec![ 36 | ("A", "B", Dual(1)), 37 | ("A", "C", Dual(2)), 38 | ("A", "D", Dual(2)), 39 | ("B", "C", Dual(1)), 40 | ("B", "D", Dual(1)), 41 | ("C", "D", Dual(2)), 42 | ]); 43 | } 44 | -------------------------------------------------------------------------------- /ascent/examples/ascent_let_clause.rs: -------------------------------------------------------------------------------- 1 | //! Binding `let` clause 2 | 3 | use ascent::ascent; 4 | 5 | #[derive(Clone, Eq, PartialEq, Hash, Debug)] 6 | pub enum List { 7 | Nil, 8 | Cons(usize, Box), 9 | } 10 | 11 | impl List { 12 | fn as_vec(&self) -> Vec { 13 | let mut items = vec![]; 14 | 15 | let mut list = self; 16 | while let Self::Cons(head, tail) = list { 17 | items.push(*head); 18 | list = tail; 19 | } 20 | 21 | items 22 | } 23 | } 24 | 25 | ascent! { 26 | // Rules: 27 | 28 | relation list(List, usize); 29 | 30 | list(List::Nil, 0); 31 | list(List::Cons(*l, Box::new(t.clone())), h) <-- list(t, l), let h = *l + 1, if h <= 5; 32 | } 33 | 34 | fn main() { 35 | let mut prog = AscentProgram::default(); 36 | 37 | prog.run(); 38 | 39 | let AscentProgram { mut list, .. } = prog; 40 | 41 | list.sort_by_key(|(_, key)| *key); 42 | 43 | let lists: Vec<_> = list.into_iter().map(|(list, len)| (list.as_vec(), len)).collect(); 44 | 45 | assert_eq!(lists, vec![ 46 | (vec![], 0), 47 | (vec![0], 1), 48 | (vec![1, 0], 2), 49 | (vec![2, 1, 0], 3), 50 | (vec![3, 2, 1, 0], 4), 51 | (vec![4, 3, 2, 1, 0], 5), 52 | ]); 53 | } 54 | -------------------------------------------------------------------------------- /ascent/examples/ascent_macros_rule.rs: -------------------------------------------------------------------------------- 1 | //! Macro rule 2 | 3 | use std::rc::Rc; 4 | 5 | use ascent::ascent; 6 | 7 | ascent! { 8 | // Facts: 9 | 10 | relation unique(isize); 11 | 12 | // Macros: 13 | 14 | macro shared($x: expr) { 15 | shared(Rc::new($x)) 16 | } 17 | 18 | // Rules: 19 | 20 | relation shared(Rc); 21 | 22 | shared!(*x) <-- unique(x); 23 | } 24 | 25 | fn main() { 26 | let mut prog = AscentProgram::default(); 27 | 28 | prog.unique = vec![(1,), (2,), (3,), (4,), (5,)]; 29 | 30 | prog.run(); 31 | 32 | let AscentProgram { mut shared, .. } = prog; 33 | 34 | shared.sort_by_key(|(key,)| Rc::clone(key)); 35 | 36 | assert_eq!(shared, vec![(Rc::new(1),), (Rc::new(2),), (Rc::new(3),), (Rc::new(4),), (Rc::new(5),),]); 37 | } 38 | -------------------------------------------------------------------------------- /ascent/examples/ascent_negation_clause.rs: -------------------------------------------------------------------------------- 1 | //! Negation clause 2 | 3 | use ascent::ascent; 4 | 5 | ascent! { 6 | // Facts: 7 | 8 | relation number(i32); 9 | 10 | // Rules: 11 | 12 | relation even(i32); 13 | 14 | even(x) <-- number(x), if x % 2 == 0; 15 | 16 | relation odd(i32); 17 | 18 | odd(x) <-- number(x), !even(x); 19 | } 20 | 21 | fn main() { 22 | let mut prog = AscentProgram::default(); 23 | 24 | prog.number = (1..=5).map(|n| (n,)).collect(); 25 | 26 | prog.run(); 27 | 28 | let AscentProgram { mut even, mut odd, .. } = prog; 29 | 30 | even.sort_by_key(|(key,)| *key); 31 | 32 | assert_eq!(even, vec![(2,), (4,),]); 33 | 34 | odd.sort_by_key(|(key,)| *key); 35 | 36 | assert_eq!(odd, vec![(1,), (3,), (5,),]); 37 | } 38 | -------------------------------------------------------------------------------- /ascent/examples/ascent_source.rs: -------------------------------------------------------------------------------- 1 | use ascent::{ascent_run, ascent_source}; 2 | 3 | mod base { 4 | ascent::ascent_source! { 5 | /// Defines `edge` and `path`, the transitive closure of `edge`. 6 | /// The type of a node is `Node` 7 | tc: 8 | 9 | relation edge(Node, Node); 10 | relation path(Node, Node); 11 | path(x, y) <-- edge(x, y); 12 | path(x, z) <-- edge(x, y), path(y, z); 13 | } 14 | } 15 | 16 | ascent_source! { symm_edges: 17 | edge(x, y) <-- edge(y, x); 18 | } 19 | 20 | fn main() { 21 | type Node = usize; 22 | let res = ascent_run! { 23 | include_source!(base::tc); 24 | include_source!(symm_edges); 25 | 26 | edge(1, 2), edge(2, 3), edge(3, 4); 27 | }; 28 | 29 | assert!(res.path.contains(&(4, 1))); 30 | } 31 | -------------------------------------------------------------------------------- /ascent/examples/context_sensitive_flow_graph.rs: -------------------------------------------------------------------------------- 1 | //! Context Sensitive Flow Graph 2 | //! 3 | //! The following example demonstrates one way of integrating context information into a control flow graph. 4 | //! 5 | //! In this example the flow relation describes a graph where each node consists of a pair 6 | //! of an instruction (`Instr`) and some (abstract) context (`Context`). 7 | //! 8 | //! Although correct, the increased number of attributes causes larger code bases, 9 | //! and thus an increased risk of typos leading to hard-to-identify bugs. 10 | //! 11 | //! The fact that each node is represented by a pair of elements can be made explicit by 12 | //! utilizing records, as demonstrated in `context_sensitive_flow_graph_with_records.rs`. 13 | //! 14 | //! (Adapted from https://souffle-lang.github.io/examples#context-sensitive-flow-graph) 15 | 16 | use ascent::ascent; 17 | 18 | #[derive(Clone, Eq, PartialEq, Hash, Debug)] 19 | pub struct Instr(&'static str); 20 | 21 | #[derive(Clone, Eq, PartialEq, Hash, Debug)] 22 | pub struct Context(&'static str); 23 | 24 | #[derive(Clone, Eq, PartialEq, Hash, Debug)] 25 | pub enum Res { 26 | Ok, 27 | Err, 28 | } 29 | 30 | ascent! { 31 | // Facts: 32 | 33 | relation succ(Instr, Context, Instr, Context); 34 | 35 | // Rules: 36 | 37 | relation flow(Instr, Context, Instr, Context); 38 | 39 | flow(i1, c1, i2, c2) <-- succ(i1, c1, i2, c2); 40 | flow(i1, c1, i3, c3) <-- flow(i1, c1, i2, c2), flow(i2, c2, i3, c3); 41 | 42 | relation res(Res); 43 | 44 | res(Res::Ok) <-- flow(Instr("w1"), Context("c1"), Instr("r2"), Context("c1")); 45 | res(Res::Err) <-- flow(Instr("w1"), Context("c1"), Instr("r2"), Context("c2")); 46 | } 47 | 48 | fn main() { 49 | let mut prog = AscentProgram::default(); 50 | 51 | prog.succ = vec![ 52 | (Instr("w1"), Context("c1"), Instr("w2"), Context("c1")), 53 | (Instr("w2"), Context("c1"), Instr("r1"), Context("c1")), 54 | (Instr("r1"), Context("c1"), Instr("r2"), Context("c1")), 55 | (Instr("w1"), Context("c2"), Instr("w2"), Context("c2")), 56 | (Instr("w2"), Context("c2"), Instr("r1"), Context("c2")), 57 | (Instr("r1"), Context("c2"), Instr("r2"), Context("c2")), 58 | ]; 59 | 60 | prog.run(); 61 | 62 | let AscentProgram { res, .. } = prog; 63 | 64 | assert_eq!(res, vec![(Res::Ok,),]); 65 | } 66 | -------------------------------------------------------------------------------- /ascent/examples/context_sensitive_flow_graph_with_records.rs: -------------------------------------------------------------------------------- 1 | //! Context Sensitive Flow Graph with Records 2 | //! 3 | //! The following example demonstrates one way of integrating context information into a control flow graph. 4 | //! 5 | //! The type program point (`ProgPoint`) aggregates an instruction and an (abstract) context (`Context`) into a new entity 6 | //! which is utilized as the node type of the flow graph. Note that in this version flow is a simpler, 7 | //! binary relation and typos mixing up instructions and contexts of different program points are effectively prevented. 8 | //! 9 | //! Also, as we will see below, the flow relation could now be modelled utilizing a generalized graph component, 10 | //! thereby inheriting a library of derived relations. 11 | //! 12 | //! (Adapted from https://souffle-lang.github.io/examples#context-sensitive-flow-graph-with-records) 13 | 14 | use ascent::ascent; 15 | 16 | #[derive(Clone, Eq, PartialEq, Hash, Debug)] 17 | pub struct Instr(&'static str); 18 | 19 | #[derive(Clone, Eq, PartialEq, Hash, Debug)] 20 | pub struct Context(&'static str); 21 | 22 | #[derive(Clone, Eq, PartialEq, Hash, Debug)] 23 | pub struct ProgPoint(Instr, Context); 24 | 25 | #[derive(Clone, Eq, PartialEq, Hash, Debug)] 26 | pub enum Res { 27 | Ok, 28 | Err, 29 | } 30 | 31 | ascent! { 32 | // Facts: 33 | 34 | relation succ(ProgPoint, ProgPoint); 35 | 36 | // Rules: 37 | 38 | relation flow(ProgPoint, ProgPoint); 39 | 40 | flow(p1, p2) <-- succ(p1, p2); 41 | flow(p1, p3) <-- flow(p1, p2), flow(p2, p3); 42 | 43 | relation res(Res); 44 | 45 | res(Res::Ok) <-- flow(ProgPoint(Instr("w1"), Context("c1")), ProgPoint(Instr("r2"), Context("c1"))); 46 | res(Res::Err) <-- flow(ProgPoint(Instr("w1"), Context("c1")), ProgPoint(Instr("r2"), Context("c2"))); 47 | } 48 | 49 | fn main() { 50 | let mut prog = AscentProgram::default(); 51 | 52 | prog.succ = vec![ 53 | (ProgPoint(Instr("w1"), Context("c1")), ProgPoint(Instr("w2"), Context("c1"))), 54 | (ProgPoint(Instr("w2"), Context("c1")), ProgPoint(Instr("r1"), Context("c1"))), 55 | (ProgPoint(Instr("r1"), Context("c1")), ProgPoint(Instr("r2"), Context("c1"))), 56 | (ProgPoint(Instr("w1"), Context("c2")), ProgPoint(Instr("w2"), Context("c2"))), 57 | (ProgPoint(Instr("w2"), Context("c2")), ProgPoint(Instr("r1"), Context("c2"))), 58 | (ProgPoint(Instr("r1"), Context("c2")), ProgPoint(Instr("r2"), Context("c2"))), 59 | ]; 60 | 61 | prog.run(); 62 | 63 | let AscentProgram { res, .. } = prog; 64 | 65 | assert_eq!(res, vec![(Res::Ok,),]); 66 | } 67 | -------------------------------------------------------------------------------- /ascent/examples/def_use_chains.rs: -------------------------------------------------------------------------------- 1 | //! DefUse Chains with Composed Types 2 | //! 3 | //! The following example utilizes a composed type to model a type hierarchy for instructions. 4 | //! 5 | //! In this example an instruction is either a `Read`` operation, a `Write` operation or a `Jump` instruction. 6 | //! 7 | //! However, to model the control flow through instructions, the flow relation needs to be able to 8 | //! cover any of those. 9 | //! 10 | //! To model this situation, the union type Instr is introduced and utilized as shown above. 11 | //! 12 | //! (Adapted from https://souffle-lang.github.io/examples#defuse-chains-with-composed-types) 13 | 14 | use ascent::ascent; 15 | 16 | #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] 17 | pub struct Var(&'static str); 18 | 19 | #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] 20 | pub struct Read(&'static str); 21 | 22 | #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] 23 | pub struct Write(&'static str); 24 | 25 | #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] 26 | pub struct Jump(&'static str); 27 | 28 | #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] 29 | pub enum Instr { 30 | Read(Read), 31 | Write(Write), 32 | Jump(Jump), 33 | } 34 | 35 | ascent! { 36 | // Facts: 37 | 38 | relation read(Read, Var); 39 | relation write(Write, Var); 40 | relation succ(Instr, Instr); 41 | 42 | // Rules: 43 | 44 | relation flow(Instr, Instr); 45 | 46 | flow(x, y) <-- succ(x, y); 47 | flow(x, z) <-- flow(x, y), flow(y, z); 48 | 49 | relation def_use(Write, Read); 50 | 51 | def_use(w, r) <-- write(w, x), read(r, x), let w2 = Instr::Write(w.clone()), let r2 = Instr::Read(r.clone()), flow(w2, r2); 52 | } 53 | 54 | fn main() { 55 | let mut prog = AscentProgram::default(); 56 | 57 | prog.read = vec![(Read("r1"), Var("v1")), (Read("r2"), Var("v1")), (Read("r3"), Var("v2"))]; 58 | 59 | prog.write = vec![(Write("w1"), Var("v1")), (Write("w2"), Var("v2")), (Write("w3"), Var("v2"))]; 60 | 61 | prog.succ = vec![ 62 | (Instr::Write(Write("w1")), Instr::Jump(Jump("o1"))), 63 | (Instr::Jump(Jump("o1")), Instr::Read(Read("r1"))), 64 | (Instr::Jump(Jump("o1")), Instr::Read(Read("r2"))), 65 | (Instr::Read(Read("r2")), Instr::Read(Read("r3"))), 66 | (Instr::Read(Read("r3")), Instr::Write(Write("w2"))), 67 | ]; 68 | 69 | prog.run(); 70 | 71 | let AscentProgram { mut def_use, .. } = prog; 72 | 73 | def_use.sort_by_key(|(key, _)| key.0); 74 | def_use.sort_by_key(|(_, key)| key.0); 75 | 76 | assert_eq!(def_use, vec![(Write("w1"), Read("r1")), (Write("w1"), Read("r2")),]); 77 | } 78 | -------------------------------------------------------------------------------- /ascent/examples/fibonacci.rs: -------------------------------------------------------------------------------- 1 | //! Conditional `if` clause 2 | 3 | use ascent::ascent; 4 | 5 | ascent! { 6 | // Facts: 7 | 8 | relation number(isize); 9 | 10 | // Rules: 11 | 12 | relation fib(isize, isize); 13 | 14 | fib(0, 1) <-- number(0); 15 | fib(1, 1) <-- number(1); 16 | fib(x, y + z) <-- number(x), if *x >= 2, fib(x - 1, y), fib(x - 2, z); 17 | } 18 | 19 | fn main() { 20 | let mut prog = AscentProgram::default(); 21 | 22 | prog.number = (0..6).map(|n| (n,)).collect(); 23 | 24 | prog.run(); 25 | 26 | let AscentProgram { mut fib, .. } = prog; 27 | 28 | fib.sort_by_key(|(key, _)| *key); 29 | 30 | assert_eq!(fib, vec![(0, 1), (1, 1), (2, 2), (3, 3), (4, 5), (5, 8),]); 31 | } 32 | -------------------------------------------------------------------------------- /ascent/examples/fizz_buzz.rs: -------------------------------------------------------------------------------- 1 | //! FizzBuzz 2 | 3 | use ascent::ascent; 4 | 5 | ascent! { 6 | // Facts: 7 | 8 | relation number(isize); 9 | 10 | // Rules: 11 | 12 | relation divisible(isize, isize); 13 | 14 | divisible(x, 3) <-- number(x), if x % 3 == 0; 15 | divisible(x, 5) <-- number(x), if x % 5 == 0; 16 | 17 | relation fizz(isize); 18 | 19 | fizz(x) <-- number(x), divisible(x, 3), !divisible(x, 5); 20 | 21 | relation buzz(isize); 22 | 23 | buzz(x) <-- number(x), !divisible(x, 3), divisible(x, 5); 24 | 25 | relation fizz_buzz(isize); 26 | 27 | fizz_buzz(x) <-- number(x), divisible(x, 3), divisible(x, 5); 28 | 29 | relation other(isize); 30 | 31 | other(x) <-- number(x), !divisible(x, 3), !divisible(x, 5); 32 | } 33 | 34 | fn main() { 35 | let mut prog = AscentProgram::default(); 36 | 37 | prog.number = (1..=15).map(|n| (n,)).collect(); 38 | 39 | prog.run(); 40 | 41 | let AscentProgram { mut fizz, mut buzz, mut fizz_buzz, mut other, .. } = prog; 42 | 43 | fizz.sort_by_key(|(key,)| *key); 44 | 45 | assert_eq!(fizz, vec![(3,), (6,), (9,), (12,),]); 46 | 47 | buzz.sort_by_key(|(key,)| *key); 48 | 49 | assert_eq!(buzz, vec![(5,), (10,),]); 50 | 51 | fizz_buzz.sort_by_key(|(key,)| *key); 52 | 53 | assert_eq!(fizz_buzz, vec![(15,),]); 54 | 55 | other.sort_by_key(|(key,)| *key); 56 | 57 | assert_eq!(other, vec![(1,), (2,), (4,), (7,), (8,), (11,), (13,), (14,)]); 58 | } 59 | -------------------------------------------------------------------------------- /ascent/examples/lists_using_recursive_enums.rs: -------------------------------------------------------------------------------- 1 | //! Lists using Recursive Enums 2 | //! 3 | //! The following example demonstrates the utilization of recursive records for 4 | //! building lists of strings over a given alphabet. 5 | //! 6 | //! The `List` type is a recursive type where each instance is either `List::Nil` 7 | //! or a pair of a leading `T` and a tailing `List::Cons`. 8 | //! 9 | //! The relation `list` is defined to contain all lists of length 5 or less 10 | //! over a given alphabet defined by the relation `char`. 11 | //! The relation `len` is essentially a function assigning each list its length. 12 | //! 13 | //! Finally, the `res` relation illustrates how to create constant values for recursive record types. 14 | //! 15 | //! (Adapted from https://souffle-lang.github.io/examples#sequences-using-recursive-records) 16 | 17 | use std::rc::Rc; 18 | 19 | use ascent::ascent; 20 | 21 | #[derive(Clone, Eq, PartialEq, Hash, Debug)] 22 | pub enum List { 23 | Cons(T, Rc>), 24 | Nil, 25 | } 26 | 27 | macro_rules! cons { 28 | ($h: expr, $t: expr) => { 29 | Rc::new(List::Cons($h, $t)) 30 | }; 31 | } 32 | 33 | macro_rules! nil { 34 | () => { 35 | Rc::new(List::Nil) 36 | }; 37 | } 38 | 39 | #[derive(Clone, Eq, PartialEq, Hash, Debug)] 40 | pub struct Res(&'static str); 41 | 42 | ascent! { 43 | // Facts: 44 | 45 | relation char(char); 46 | 47 | // Rules: 48 | 49 | relation list(Rc>); 50 | 51 | list(nil!()); 52 | list(cons!(c.clone(), l.clone())) <-- char(c), list(l), len(l, n), if *n < 5; 53 | 54 | relation len(Rc>, usize); 55 | 56 | len(nil!(), 0); 57 | len(l.clone(), n + 1) <-- char(c), len(r, n), let l = cons!(c.clone(), r.clone()), list(&l); 58 | 59 | relation res(Res); 60 | 61 | res(Res("-")) <-- list(nil!()); 62 | res(Res("a")) <-- list(cons!('a', nil!())); 63 | res(Res("b")) <-- list(cons!('b', nil!())); 64 | res(Res("c")) <-- list(cons!('c', nil!())); 65 | res(Res("ab")) <-- list(cons!('a', cons!('b', nil!()))); 66 | res(Res("aba")) <-- list(cons!('a', cons!('b', cons!('a', nil!())))); 67 | res(Res("abc")) <-- list(cons!('a', cons!('b', cons!('c', nil!())))); 68 | } 69 | 70 | fn main() { 71 | let mut prog = AscentProgram::default(); 72 | 73 | prog.char = vec![('a',), ('b',)]; 74 | 75 | prog.run(); 76 | 77 | let AscentProgram { mut res, .. } = prog; 78 | 79 | res.sort_by_key(|(key,)| key.0); 80 | 81 | assert_eq!(res, vec![(Res("-"),), (Res("a"),), (Res("ab"),), (Res("aba"),), (Res("b"),),]); 82 | } 83 | -------------------------------------------------------------------------------- /ascent/examples/transitive_graph_closure.rs: -------------------------------------------------------------------------------- 1 | //! Transitive Graph Closure 2 | //! 3 | //! The following example encodes the transitive closure of a directed graph. 4 | 5 | use ascent::ascent; 6 | 7 | #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] 8 | pub struct Node(&'static str); 9 | 10 | ascent! { 11 | // Facts: 12 | 13 | relation node(Node); 14 | relation edge(Node, Node); 15 | 16 | // Rules: 17 | 18 | relation reachable(Node, Node); 19 | 20 | // The transitive closure can be written using a so-called linear rule: 21 | reachable(x, y) <-- edge(x, y); 22 | reachable(x, z) <-- reachable(x, y), edge(y, z); 23 | 24 | // or a non-linear rule: 25 | // reachable(x, y) <-- edge(x, y); 26 | // reachable(x, z) <-- reachable(x, y), reachable(y, z); 27 | 28 | // While both variants are semantically equivalent the linear rule 29 | // tends to be more performant since in the non-linear variant the fact `reachable(x, y)` 30 | // is redundantly discovered again at every iteration (and thus n − 1 times). 31 | 32 | relation closure_of_a(Node); 33 | 34 | closure_of_a(y) <-- reachable(Node("A"), y); 35 | } 36 | 37 | fn main() { 38 | let mut prog = AscentProgram::default(); 39 | 40 | prog.node = vec![(Node("A"),), (Node("B"),), (Node("C"),)]; 41 | 42 | prog.edge = vec![(Node("A"), Node("B")), (Node("B"), Node("C"))]; 43 | 44 | prog.run(); 45 | 46 | let AscentProgram { mut reachable, mut closure_of_a, .. } = prog; 47 | 48 | reachable.sort_by_key(|(key, _)| key.0); 49 | reachable.sort_by_key(|(_, key)| key.0); 50 | 51 | assert_eq!(reachable, vec![(Node("A"), Node("B")), (Node("A"), Node("C")), (Node("B"), Node("C")),]); 52 | 53 | closure_of_a.sort_by_key(|(key,)| key.0); 54 | 55 | assert_eq!(closure_of_a, vec![(Node("B"),), (Node("C"),),]); 56 | } 57 | -------------------------------------------------------------------------------- /ascent/examples/var_points_to.rs: -------------------------------------------------------------------------------- 1 | //! Simple Typed VarPointsTo 2 | //! 3 | //! The following example encodes the most simple version of a var-points-to analysis. 4 | //! 5 | //! The example starts by declaring types for named variables, objects and fields. 6 | //! Based on those, four input relations `assign`, `new`, `ld` and `st` are declared and 7 | //! filled with data corresponding to the small code snippet outlined below: 8 | //! 9 | //! ``` 10 | //! let mut v1 = h1(); 11 | //! 12 | //! let v2 = h2(); 13 | //! v1 = v2; 14 | //! 15 | //! let v3 = h3(); 16 | //! v1.f = v3; 17 | //! 18 | //! let v4 = v1.f; 19 | //! ``` 20 | //! 21 | //! The analysis itself is broken up in two parts: 22 | //! 23 | //! - computation of aliases 24 | //! - computation of the var-points-to relation based on aliases 25 | //! 26 | //! Note that in particular for the last rule of the alias relation the utilization 27 | //! of typed attributes ensures that connections between attributes are consistently established. 28 | //! 29 | //! Problems caused by e.g. getting the wrong order of parameters can be effectively prevented. 30 | //! 31 | //! (Adapted from https://souffle-lang.github.io/examples#simple-typed-varpointsto) 32 | 33 | use ascent::ascent; 34 | 35 | #[derive(Clone, Eq, PartialEq, Hash, Debug)] 36 | pub struct Var(&'static str); 37 | 38 | #[derive(Clone, Eq, PartialEq, Hash, Debug)] 39 | pub struct Obj(&'static str); 40 | 41 | #[derive(Clone, Eq, PartialEq, Hash, Debug)] 42 | pub struct Field(&'static str); 43 | 44 | ascent! { 45 | // Facts: 46 | 47 | relation assign(Var, Var); 48 | relation new(Var, Obj); 49 | relation ld(Var, Var, Field); 50 | relation st(Var, Field, Var); 51 | 52 | // Rules: 53 | 54 | relation alias(Var, Var); 55 | 56 | alias(x, x) <-- assign(x, _); 57 | alias(x, x) <-- assign(_, x); 58 | alias(x, y) <-- assign(x, y); 59 | alias(x, y) <-- ld(x, a, f), alias(a, b), st(b, f, y); 60 | 61 | relation points_to(Var, Obj); 62 | 63 | points_to(x, y) <-- new(x, y); 64 | points_to(x, y) <-- alias(x, z), points_to(z, y); 65 | } 66 | 67 | fn main() { 68 | let mut prog = AscentProgram::default(); 69 | 70 | prog.assign = vec![(Var("v1"), Var("v2"))]; 71 | 72 | prog.new = vec![(Var("v1"), Obj("h1")), (Var("v2"), Obj("h2")), (Var("v3"), Obj("h3"))]; 73 | 74 | prog.st = vec![(Var("v1"), Field("f"), Var("v3"))]; 75 | 76 | prog.ld = vec![(Var("v4"), Var("v1"), Field("f"))]; 77 | 78 | prog.run(); 79 | 80 | let AscentProgram { mut alias, mut points_to, .. } = prog; 81 | 82 | alias.sort_by_key(|(_, key)| key.0); 83 | alias.sort_by_key(|(key, _)| key.0); 84 | 85 | assert_eq!(alias, vec![ 86 | (Var("v1"), Var("v1")), 87 | (Var("v1"), Var("v2")), 88 | (Var("v2"), Var("v2")), 89 | (Var("v4"), Var("v3")), 90 | ]); 91 | 92 | points_to.sort_by_key(|(_, key)| key.0); 93 | points_to.sort_by_key(|(key, _)| key.0); 94 | 95 | assert_eq!(points_to, vec![ 96 | (Var("v1"), Obj("h1")), 97 | (Var("v1"), Obj("h2")), 98 | (Var("v2"), Obj("h2")), 99 | (Var("v3"), Obj("h3")), 100 | (Var("v4"), Obj("h3")), 101 | ]); 102 | } 103 | -------------------------------------------------------------------------------- /ascent/src/aggregators.rs: -------------------------------------------------------------------------------- 1 | //! This module provides aggregators that can be used in Ascent rules. 2 | //! 3 | //! eg: Writing the average of `foo` in `bar` 4 | //! ``` 5 | //! use ascent::ascent; 6 | //! use ascent::aggregators::*; 7 | //! ascent!{ 8 | //! relation foo(i32); 9 | //! relation bar(i32); 10 | //! // ... 11 | //! bar(m as i32) <-- agg m = mean(x) in foo(x); 12 | //! } 13 | //! ``` 14 | 15 | use std::iter::Sum; 16 | use std::ops::Add; 17 | 18 | /// computes the minimum of the input column 19 | pub fn min<'a, N>(inp: impl Iterator) -> impl Iterator 20 | where N: 'a + Ord + Clone { 21 | inp.map(|tuple| tuple.0).min().cloned().into_iter() 22 | } 23 | 24 | /// computes the maximum of the input column 25 | pub fn max<'a, N>(inp: impl Iterator) -> impl Iterator 26 | where N: 'a + Ord + Clone { 27 | inp.map(|tuple| tuple.0).max().cloned().into_iter() 28 | } 29 | 30 | /// computes the sum of the input column 31 | pub fn sum<'a, N>(inp: impl Iterator) -> impl Iterator 32 | where N: 'a + Ord + Add + Clone + Sum { 33 | let sum = inp.map(|tuple| tuple.0).cloned().sum::(); 34 | std::iter::once(sum) 35 | } 36 | 37 | /// returns the number of tuples 38 | /// 39 | /// # Examples 40 | /// 41 | /// ``` 42 | /// # use ascent::ascent_run; 43 | /// # use ascent::aggregators::count; 44 | /// let res = ascent_run!{ 45 | /// relation edge(u32, u32); 46 | /// relation path(u32, u32); 47 | /// relation num_paths(usize); 48 | /// path(a, b) <-- edge(a, b); 49 | /// path(a, c) <-- path(a, b), edge(b, c); 50 | /// 51 | /// edge(1, 2); 52 | /// edge(2, 3); 53 | /// edge(3, 4); 54 | /// 55 | /// num_paths(n) <-- agg n = count() in path(_, _); 56 | /// }; 57 | /// // This example program is expected to produce 6 paths. 58 | /// assert_eq!(res.num_paths[0].0, 6); 59 | ///``` 60 | pub fn count(inp: impl Iterator) -> impl Iterator { 61 | let (size_floor, size_ceiling) = inp.size_hint(); 62 | let size_ceiling = size_ceiling.unwrap_or(usize::MAX); 63 | let count = if size_floor == size_ceiling { size_floor } else { inp.count() }; 64 | std::iter::once(count) 65 | } 66 | 67 | /// computes the average of the input column, returning an `f64` 68 | pub fn mean<'a, N>(inp: impl Iterator) -> impl Iterator 69 | where N: 'a + Clone + Into { 70 | let (sum, count) = inp.fold((0.0, 0usize), |(sum, count), tuple| (tuple.0.clone().into() + sum, count + 1)); 71 | let res = if count == 0 { None } else { Some(sum / count as f64) }; 72 | res.into_iter() 73 | } 74 | 75 | /// computes the value at the given percentile of the input column 76 | pub fn percentile<'a, TItem, TInputIter>(p: f64) -> impl Fn(TInputIter) -> std::option::IntoIter 77 | where 78 | TInputIter: Iterator, 79 | TItem: 'a + Ord + Clone, 80 | { 81 | move |inp| { 82 | let mut sorted: Vec<_> = inp.map(|tuple| tuple.0.clone()).collect(); 83 | sorted.sort(); 84 | let p_index = (sorted.len() as f64 * p / 100.0) as usize; 85 | if !sorted.is_empty() { Some(sorted.swap_remove(p_index)) } else { None }.into_iter() 86 | } 87 | } 88 | 89 | /// backs negations (eg `!foo(x)`) in `ascent` 90 | pub fn not(mut inp: impl Iterator) -> impl Iterator { 91 | let any = inp.next().is_some(); 92 | if any { None } else { Some(()) }.into_iter() 93 | } 94 | -------------------------------------------------------------------------------- /ascent/src/c_rel_index_combined.rs: -------------------------------------------------------------------------------- 1 | use rayon::prelude::*; 2 | 3 | use crate::internal::{CRelIndexRead, CRelIndexReadAll}; 4 | use crate::rel_index_read::RelIndexCombined; 5 | 6 | impl<'a, Ind1, Ind2, K, V> CRelIndexRead<'a> for RelIndexCombined<'a, Ind1, Ind2> 7 | where 8 | Ind1: CRelIndexRead<'a, Key = K, Value = V>, 9 | Ind2: CRelIndexRead<'a, Key = K, Value = V>, 10 | { 11 | type Key = K; 12 | 13 | type Value = V; 14 | 15 | type IteratorType = rayon::iter::Chain< 16 | rayon::iter::Flatten>::IteratorType>>, 17 | rayon::iter::Flatten>::IteratorType>>, 18 | >; 19 | 20 | fn c_index_get(&'a self, key: &Self::Key) -> Option { 21 | match (self.ind1.c_index_get(key), self.ind2.c_index_get(key)) { 22 | (None, None) => None, 23 | (iter1, iter2) => { 24 | let res = iter1.into_par_iter().flatten().chain(iter2.into_par_iter().flatten()); 25 | Some(res) 26 | }, 27 | } 28 | } 29 | } 30 | 31 | impl<'a, Ind1, Ind2, K: 'a, V: 'a, VTI: ParallelIterator + 'a> CRelIndexReadAll<'a> 32 | for RelIndexCombined<'a, Ind1, Ind2> 33 | where 34 | Ind1: CRelIndexReadAll<'a, Key = K, ValueIteratorType = VTI>, 35 | Ind2: CRelIndexReadAll<'a, Key = K, ValueIteratorType = VTI>, 36 | { 37 | type Key = K; 38 | type Value = V; 39 | 40 | type ValueIteratorType = VTI; 41 | type AllIteratorType = rayon::iter::Chain; 42 | 43 | fn c_iter_all(&'a self) -> Self::AllIteratorType { self.ind1.c_iter_all().chain(self.ind2.c_iter_all()) } 44 | } 45 | -------------------------------------------------------------------------------- /ascent/src/c_rel_index_read.rs: -------------------------------------------------------------------------------- 1 | use rayon::iter::ParallelIterator; 2 | 3 | pub trait CRelIndexRead<'a> { 4 | type Key; 5 | type Value; 6 | type IteratorType: ParallelIterator + Clone + 'a; 7 | fn c_index_get(&'a self, key: &Self::Key) -> Option; 8 | } 9 | 10 | pub trait CRelIndexReadAll<'a> { 11 | type Key: 'a; 12 | type Value; 13 | type ValueIteratorType: ParallelIterator + 'a; 14 | type AllIteratorType: ParallelIterator + 'a; 15 | fn c_iter_all(&'a self) -> Self::AllIteratorType; 16 | } 17 | -------------------------------------------------------------------------------- /ascent/src/c_rel_no_index.rs: -------------------------------------------------------------------------------- 1 | use dashmap::RwLock; 2 | use instant::Instant; 3 | use rayon::prelude::{IntoParallelRefIterator, ParallelIterator}; 4 | 5 | use crate::internal::{ 6 | CRelIndexRead, CRelIndexReadAll, CRelIndexWrite, Freezable, RelIndexMerge, RelIndexRead, RelIndexReadAll, 7 | RelIndexWrite, 8 | }; 9 | 10 | pub struct CRelNoIndex { 11 | // TODO remove pub 12 | pub vec: Vec>>, 13 | // vec: [RwLock>; 32], 14 | frozen: bool, 15 | } 16 | 17 | impl Default for CRelNoIndex { 18 | #[inline] 19 | fn default() -> Self { 20 | let threads = rayon::current_num_threads().max(1); 21 | let mut vec = Vec::with_capacity(threads); 22 | for _ in 0..threads { 23 | vec.push(RwLock::new(vec![])); 24 | } 25 | Self { vec, frozen: false } 26 | 27 | // Self { vec: array_init::array_init(|_| RwLock::new(vec![])), frozen: false } 28 | } 29 | } 30 | 31 | impl CRelNoIndex { 32 | pub fn hash_usize(&self, _key: &()) -> usize { 0 } 33 | } 34 | 35 | impl Freezable for CRelNoIndex { 36 | fn freeze(&mut self) { self.frozen = true; } 37 | fn unfreeze(&mut self) { self.frozen = false; } 38 | } 39 | 40 | impl<'a, V: 'a> RelIndexRead<'a> for CRelNoIndex { 41 | type Key = (); 42 | type Value = &'a V; 43 | 44 | type IteratorType = std::iter::FlatMap< 45 | std::slice::Iter<'a, RwLock>>, 46 | std::slice::Iter<'a, V>, 47 | fn(&RwLock>) -> std::slice::Iter, 48 | >; 49 | 50 | fn index_get(&'a self, _key: &Self::Key) -> Option { 51 | assert!(self.frozen); 52 | let res: Self::IteratorType = self.vec.iter().flat_map(|v| { 53 | let data = unsafe { &*v.data_ptr() }; 54 | data.iter() 55 | }); 56 | Some(res) 57 | } 58 | 59 | #[inline(always)] 60 | fn len_estimate(&self) -> usize { 1 } 61 | 62 | fn is_empty(&'a self) -> bool { false } 63 | } 64 | 65 | impl<'a, V: 'a + Sync + Send> CRelIndexRead<'a> for CRelNoIndex { 66 | type Key = (); 67 | type Value = &'a V; 68 | 69 | type IteratorType = 70 | rayon::iter::FlatMap>>, fn(&RwLock>) -> rayon::slice::Iter>; 71 | 72 | fn c_index_get(&'a self, _key: &Self::Key) -> Option { 73 | assert!(self.frozen); 74 | let res: Self::IteratorType = self.vec.par_iter().flat_map(|v| { 75 | let data = unsafe { &*v.data_ptr() }; 76 | data.par_iter() 77 | }); 78 | Some(res) 79 | } 80 | } 81 | 82 | impl RelIndexWrite for CRelNoIndex { 83 | type Key = (); 84 | type Value = V; 85 | 86 | fn index_insert(&mut self, _key: Self::Key, value: Self::Value) { 87 | // not necessary because we have a mut reference 88 | // assert!(!ind.frozen); 89 | let shard_idx = rayon::current_thread_index().unwrap_or(0) % self.vec.len(); 90 | self.vec[shard_idx].get_mut().push(value); 91 | } 92 | } 93 | 94 | impl RelIndexMerge for CRelNoIndex { 95 | fn move_index_contents(from: &mut Self, to: &mut Self) { 96 | let before = Instant::now(); 97 | assert_eq!(from.len_estimate(), to.len_estimate()); 98 | // not necessary because we have a mut reference 99 | // assert!(!from.frozen); 100 | // assert!(!to.frozen); 101 | 102 | from.vec.iter_mut().zip(to.vec.iter_mut()).for_each(|(from, to)| { 103 | let from = from.get_mut(); 104 | let to = to.get_mut(); 105 | 106 | if from.len() > to.len() { 107 | std::mem::swap(from, to); 108 | } 109 | to.append(from); 110 | }); 111 | unsafe { 112 | crate::internal::MOVE_NO_INDEX_CONTENTS_TOTAL_TIME += before.elapsed(); 113 | } 114 | } 115 | } 116 | 117 | impl CRelIndexWrite for CRelNoIndex { 118 | type Key = (); 119 | type Value = V; 120 | 121 | fn index_insert(&self, _key: Self::Key, value: Self::Value) { 122 | assert!(!self.frozen); 123 | let shard_idx = rayon::current_thread_index().unwrap_or(0) % self.vec.len(); 124 | self.vec[shard_idx].write().push(value); 125 | } 126 | } 127 | 128 | impl<'a, V: 'a> RelIndexReadAll<'a> for CRelNoIndex { 129 | type Key = &'a (); 130 | type Value = &'a V; 131 | 132 | type ValueIteratorType = >::IteratorType; 133 | 134 | type AllIteratorType = std::iter::Once<(&'a (), Self::ValueIteratorType)>; 135 | 136 | fn iter_all(&'a self) -> Self::AllIteratorType { std::iter::once((&(), self.index_get(&()).unwrap())) } 137 | } 138 | 139 | impl<'a, V: 'a + Sync + Send> CRelIndexReadAll<'a> for CRelNoIndex { 140 | type Key = &'a (); 141 | type Value = &'a V; 142 | 143 | type ValueIteratorType = >::IteratorType; 144 | 145 | type AllIteratorType = rayon::iter::Once<(&'a (), Self::ValueIteratorType)>; 146 | 147 | fn c_iter_all(&'a self) -> Self::AllIteratorType { rayon::iter::once((&(), self.c_index_get(&()).unwrap())) } 148 | } 149 | -------------------------------------------------------------------------------- /ascent/src/convert.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | use std::sync::Arc; 3 | 4 | pub trait Convert { 5 | fn convert(source: TSource) -> Self; 6 | } 7 | 8 | impl Convert for T { 9 | #[inline(always)] 10 | fn convert(source: T) -> T { source } 11 | } 12 | 13 | impl Convert<&T> for T 14 | where T: Clone 15 | { 16 | #[inline(always)] 17 | fn convert(source: &T) -> T { source.clone() } 18 | } 19 | 20 | impl Convert<&str> for String { 21 | fn convert(source: &str) -> Self { source.to_string() } 22 | } 23 | 24 | impl Convert<&Rc> for T { 25 | fn convert(source: &Rc) -> Self { source.as_ref().clone() } 26 | } 27 | 28 | impl Convert<&Arc> for T { 29 | fn convert(source: &Arc) -> Self { source.as_ref().clone() } 30 | } 31 | -------------------------------------------------------------------------------- /ascent/src/exps.rs: -------------------------------------------------------------------------------- 1 | // TODO delete this file 2 | #![cfg(all(test, feature = "par"))] 3 | #![allow(dead_code)] 4 | 5 | use std::sync::Mutex; 6 | use std::sync::atomic::AtomicBool; 7 | use std::sync::atomic::Ordering::Relaxed; 8 | use std::time::Instant; 9 | 10 | use rayon::prelude::*; 11 | 12 | use crate::c_rel_index::CRelIndex; 13 | use crate::internal::{Freezable, RelIndexWrite}; 14 | use crate::rel_index_read::RelIndexRead; 15 | 16 | // #[test] 17 | fn bench_aovec() { 18 | type AOVec = boxcar::Vec; 19 | let size = 125_000_000; 20 | 21 | println!("pushing ..."); 22 | let before = Instant::now(); 23 | let mut vec = vec![]; 24 | for i in 0..size { 25 | vec.push(i); 26 | } 27 | let elapsed = before.elapsed(); 28 | println!("vec time: {:?}", elapsed); 29 | 30 | let before = Instant::now(); 31 | let vec = AOVec::new(); 32 | for i in 0..size { 33 | vec.push(i); 34 | } 35 | let elapsed = before.elapsed(); 36 | println!("ao vec time: {:?}", elapsed); 37 | 38 | ///////////////////////////////// 39 | 40 | println!("\nparallel pushing ..."); 41 | 42 | let before = Instant::now(); 43 | let vec = Mutex::new(vec![]); 44 | (0..size).into_par_iter().for_each(|i| { 45 | vec.lock().unwrap().push(i); 46 | }); 47 | let elapsed = before.elapsed(); 48 | assert_eq!(vec.lock().unwrap().len(), size); 49 | println!("parallel Mutex time: {:?}", elapsed); 50 | 51 | let before = Instant::now(); 52 | let vec = AOVec::new(); 53 | (0..size).into_par_iter().for_each(|i| { 54 | vec.push(i); 55 | }); 56 | let elapsed = before.elapsed(); 57 | assert_eq!(vec.len(), size); 58 | println!("parallel ao vec time: {:?}", elapsed); 59 | } 60 | 61 | // #[test] 62 | fn bench_atomic_changed() { 63 | type AOVec = boxcar::Vec; 64 | let size = 125_000_000; 65 | 66 | { 67 | let before = Instant::now(); 68 | let vec = AOVec::new(); 69 | let changed = AtomicBool::new(false); 70 | (0..size).into_par_iter().for_each(|i| { 71 | vec.push(i); 72 | changed.store(true, Relaxed); 73 | }); 74 | let elapsed = before.elapsed(); 75 | println!("changed: {}", changed.load(Relaxed)); 76 | assert_eq!(vec.len(), size); 77 | println!("atomic changed ao vec time: {:?}", elapsed); 78 | } 79 | 80 | { 81 | let before = Instant::now(); 82 | let vec = AOVec::new(); 83 | let changed = (0..size).into_par_iter().fold_with(false, |_changed, i| { 84 | vec.push(i); 85 | true 86 | }); 87 | // let changed = changed.reduce(|| false, |x, y| x | y); 88 | println!("changed count: {}", changed.count()); 89 | let elapsed = before.elapsed(); 90 | // println!("changed: {}", changed); 91 | assert_eq!(vec.len(), size); 92 | println!("therad-local changed ao vec time: {:?}", elapsed); 93 | } 94 | } 95 | 96 | // #[test] 97 | fn bench_crel_index() { 98 | let mut rel_index = CRelIndex::default(); 99 | 100 | let before = Instant::now(); 101 | for i in 0..1_000_000 { 102 | RelIndexWrite::index_insert(&mut rel_index, i, i); 103 | } 104 | let elapsed = before.elapsed(); 105 | println!("insert time: {:?}", elapsed); 106 | 107 | let iters = 1_000_000; 108 | 109 | let before = Instant::now(); 110 | let mut _sum = 0; 111 | for _ in 0..iters { 112 | crate::internal::Freezable::freeze(&mut rel_index as _); 113 | _sum += rel_index.index_get(&42).unwrap().next().unwrap(); 114 | rel_index.unfreeze(); 115 | } 116 | 117 | let elapsed = before.elapsed(); 118 | 119 | println!("freeze_unfreeze for {} iterations time: {:?}", iters, elapsed); 120 | } 121 | 122 | // #[test] 123 | fn bench_par_iter() { 124 | let arr = (1..1_000_000).collect::>(); 125 | 126 | let before = Instant::now(); 127 | arr.par_iter().for_each(|x| { 128 | if *x == 42 { 129 | println!("x is 42"); 130 | } 131 | }); 132 | println!("par_iter took {:?}", before.elapsed()); 133 | 134 | let before = Instant::now(); 135 | arr.iter().par_bridge().for_each(|x| { 136 | if *x == 42 { 137 | println!("x is 42"); 138 | } 139 | }); 140 | println!("par_bridge took {:?}", before.elapsed()); 141 | } 142 | 143 | #[test] 144 | fn bench_par_flat_map() { 145 | fn calc_sum(x: usize) -> usize { 146 | let mut res = 0; 147 | for i in 0..=(x >> 5) { 148 | assert!(res < usize::MAX); 149 | res += i; 150 | } 151 | res 152 | } 153 | 154 | let len = 40; 155 | let mut arr = Vec::with_capacity(len); 156 | 157 | for _ in 0..len { 158 | let vec = (0..1000).collect::>(); 159 | arr.push(vec); 160 | } 161 | 162 | let before = Instant::now(); 163 | arr.iter().flat_map(|v| v.iter()).for_each(|x| { 164 | calc_sum(*x); 165 | }); 166 | println!("ser flat_map took {:?}", before.elapsed()); 167 | 168 | let before = Instant::now(); 169 | arr.par_iter().flat_map(|v| v.par_iter()).for_each(|x| { 170 | calc_sum(*x); 171 | }); 172 | println!("par flat_map took {:?}", before.elapsed()); 173 | 174 | let before = Instant::now(); 175 | arr.par_iter().flat_map_iter(|v| v.iter()).for_each(|x| { 176 | calc_sum(*x); 177 | }); 178 | println!("par flat_map_iter took {:?}", before.elapsed()); 179 | } 180 | 181 | #[test] 182 | fn exp_rayon_scope() { 183 | rayon::scope(|__scope| { 184 | __scope.spawn(|_| {}); 185 | __scope.spawn(|_| {}); 186 | }); 187 | } 188 | -------------------------------------------------------------------------------- /ascent/src/internal.rs: -------------------------------------------------------------------------------- 1 | //! Provides definitions required for the `ascent` macro(s), plus traits that custom relations need to implement. 2 | 3 | use std::collections::{HashMap, HashSet}; 4 | use std::hash::{BuildHasherDefault, Hash}; 5 | use std::time::Duration; 6 | 7 | use ascent_base::Lattice; 8 | use cfg_if::cfg_if; 9 | pub use instant::Instant; 10 | use rustc_hash::FxHasher; 11 | 12 | pub use crate::convert::*; 13 | pub use crate::rel_index_read::{RelIndexCombined, RelIndexRead, RelIndexReadAll}; 14 | 15 | pub type RelIndexType = RelIndexType1; 16 | 17 | pub type LatticeIndexType = HashMap>, BuildHasherDefault>; 18 | 19 | pub(crate) type HashBrownRelFullIndexType = hashbrown::HashMap>; 20 | pub type RelFullIndexType = HashBrownRelFullIndexType; 21 | 22 | pub type RelNoIndexType = Vec; 23 | 24 | cfg_if! { 25 | if #[cfg(feature = "par")] { 26 | pub use crate::c_rel_index_read::CRelIndexRead; 27 | pub use crate::c_rel_index_read::CRelIndexReadAll; 28 | 29 | pub use crate::c_rel_index::shards_count; 30 | 31 | pub use crate::c_rel_index::CRelIndex; 32 | pub use crate::c_rel_full_index::CRelFullIndex; 33 | pub use crate::c_lat_index::CLatIndex; 34 | pub use crate::c_rel_no_index::CRelNoIndex; 35 | pub use crate::c_rel_index::DashMapViewParIter; 36 | } 37 | } 38 | 39 | pub use crate::to_rel_index::{ToRelIndex, ToRelIndex0}; 40 | pub use crate::tuple_of_borrowed::TupleOfBorrowed; 41 | 42 | pub trait Freezable { 43 | fn freeze(&mut self) {} 44 | fn unfreeze(&mut self) {} 45 | } 46 | 47 | pub trait RelIndexWrite: Sized { 48 | type Key; 49 | type Value; 50 | fn index_insert(&mut self, key: Self::Key, value: Self::Value); 51 | } 52 | 53 | pub trait RelIndexMerge: Sized { 54 | fn move_index_contents(from: &mut Self, to: &mut Self); 55 | fn merge_delta_to_total_new_to_delta(new: &mut Self, delta: &mut Self, total: &mut Self) { 56 | Self::move_index_contents(delta, total); 57 | std::mem::swap(new, delta); 58 | } 59 | 60 | /// Called once at the start of the SCC 61 | #[allow(unused_variables)] 62 | fn init(new: &mut Self, delta: &mut Self, total: &mut Self) {} 63 | } 64 | 65 | pub trait CRelIndexWrite { 66 | type Key; 67 | type Value; 68 | fn index_insert(&self, key: Self::Key, value: Self::Value); 69 | } 70 | 71 | pub trait RelFullIndexRead<'a> { 72 | type Key; 73 | fn contains_key(&'a self, key: &Self::Key) -> bool; 74 | } 75 | 76 | pub trait RelFullIndexWrite { 77 | type Key: Clone; 78 | type Value; 79 | /// if an entry for `key` does not exist, inserts `v` for it and returns true. 80 | fn insert_if_not_present(&mut self, key: &Self::Key, v: Self::Value) -> bool; 81 | } 82 | 83 | pub trait CRelFullIndexWrite { 84 | type Key: Clone; 85 | type Value; 86 | /// if an entry for `key` does not exist, inserts `v` for it and returns true. 87 | fn insert_if_not_present(&self, key: &Self::Key, v: Self::Value) -> bool; 88 | } 89 | 90 | pub type RelIndexType1 = HashMap, BuildHasherDefault>; 91 | 92 | pub static mut MOVE_REL_INDEX_CONTENTS_TOTAL_TIME: Duration = Duration::ZERO; 93 | pub static mut INDEX_INSERT_TOTAL_TIME: Duration = Duration::ZERO; 94 | 95 | impl RelIndexWrite for RelIndexType1 { 96 | type Key = K; 97 | type Value = V; 98 | 99 | fn index_insert(&mut self, key: K, value: V) { 100 | // let before = Instant::now(); 101 | use std::collections::hash_map::Entry::*; 102 | match self.entry(key) { 103 | Occupied(mut vec) => vec.get_mut().push(value), 104 | Vacant(vacant) => { 105 | let mut vec = Vec::with_capacity(4); 106 | vec.push(value); 107 | vacant.insert(vec); 108 | }, 109 | } 110 | // unsafe { 111 | // INDEX_INSERT_TOTAL_TIME += before.elapsed(); 112 | // } 113 | } 114 | } 115 | 116 | impl RelIndexMerge for RelIndexType1 { 117 | fn move_index_contents(from: &mut RelIndexType1, to: &mut RelIndexType1) { 118 | let before = Instant::now(); 119 | if from.len() > to.len() { 120 | std::mem::swap(from, to); 121 | } 122 | use std::collections::hash_map::Entry::*; 123 | for (k, mut v) in from.drain() { 124 | match to.entry(k) { 125 | Occupied(existing) => { 126 | let existing = existing.into_mut(); 127 | if v.len() > existing.len() { 128 | std::mem::swap(&mut v, existing); 129 | } 130 | existing.append(&mut v); 131 | }, 132 | Vacant(vacant) => { 133 | vacant.insert(v); 134 | }, 135 | } 136 | } 137 | unsafe { 138 | MOVE_REL_INDEX_CONTENTS_TOTAL_TIME += before.elapsed(); 139 | } 140 | } 141 | } 142 | 143 | impl RelIndexWrite for RelNoIndexType { 144 | type Key = (); 145 | type Value = usize; 146 | 147 | fn index_insert(&mut self, _key: Self::Key, tuple_index: usize) { self.push(tuple_index); } 148 | } 149 | 150 | impl RelIndexMerge for RelNoIndexType { 151 | fn move_index_contents(ind1: &mut Self, ind2: &mut Self) { ind2.append(ind1); } 152 | } 153 | 154 | impl RelIndexWrite for LatticeIndexType { 155 | type Key = K; 156 | type Value = V; 157 | 158 | #[inline(always)] 159 | fn index_insert(&mut self, key: Self::Key, tuple_index: V) { self.entry(key).or_default().insert(tuple_index); } 160 | } 161 | 162 | impl RelIndexMerge for LatticeIndexType { 163 | #[inline(always)] 164 | fn move_index_contents(hm1: &mut LatticeIndexType, hm2: &mut LatticeIndexType) { 165 | for (k, v) in hm1.drain() { 166 | let set = hm2.entry(k).or_default(); 167 | set.extend(v); 168 | } 169 | } 170 | } 171 | 172 | pub static mut MOVE_FULL_INDEX_CONTENTS_TOTAL_TIME: Duration = Duration::ZERO; 173 | pub static mut MOVE_NO_INDEX_CONTENTS_TOTAL_TIME: Duration = Duration::ZERO; 174 | 175 | impl RelIndexWrite for HashBrownRelFullIndexType { 176 | type Key = K; 177 | type Value = V; 178 | 179 | #[inline(always)] 180 | fn index_insert(&mut self, key: Self::Key, value: V) { self.insert(key, value); } 181 | } 182 | 183 | impl RelIndexMerge for HashBrownRelFullIndexType { 184 | fn move_index_contents(from: &mut Self, to: &mut Self) { 185 | let before = Instant::now(); 186 | if from.len() > to.len() { 187 | std::mem::swap(from, to); 188 | } 189 | to.reserve(from.len()); 190 | for (k, v) in from.drain() { 191 | to.insert(k, v); // TODO could be improved 192 | } 193 | unsafe { 194 | MOVE_FULL_INDEX_CONTENTS_TOTAL_TIME += before.elapsed(); 195 | } 196 | } 197 | } 198 | 199 | impl RelFullIndexWrite for HashBrownRelFullIndexType { 200 | type Key = K; 201 | type Value = V; 202 | #[inline] 203 | fn insert_if_not_present(&mut self, key: &K, v: V) -> bool { 204 | match self.raw_entry_mut().from_key(key) { 205 | hashbrown::hash_map::RawEntryMut::Occupied(_) => false, 206 | hashbrown::hash_map::RawEntryMut::Vacant(vacant) => { 207 | vacant.insert(key.clone(), v); 208 | true 209 | }, 210 | } 211 | } 212 | } 213 | 214 | impl RelFullIndexRead<'_> for HashBrownRelFullIndexType { 215 | type Key = K; 216 | 217 | fn contains_key(&self, key: &Self::Key) -> bool { self.contains_key(key) } 218 | } 219 | 220 | /// type constraints for relation columns 221 | pub struct TypeConstraints 222 | where T: Clone + Eq + Hash 223 | { 224 | _t: ::core::marker::PhantomData, 225 | } 226 | /// type constraints for a lattice 227 | pub struct LatTypeConstraints 228 | where T: Clone + Eq + Hash + Lattice 229 | { 230 | _t: ::core::marker::PhantomData, 231 | } 232 | 233 | /// type constraints for parallel Ascent 234 | pub struct ParTypeConstraints 235 | where T: Send + Sync 236 | { 237 | _t: ::core::marker::PhantomData, 238 | } 239 | 240 | #[inline(always)] 241 | pub fn comment(_: &str) {} 242 | -------------------------------------------------------------------------------- /ascent/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Ascent enables writing logic programs in the style of Datalog in Rust. 2 | //! 3 | //! See the documentation for [`ascent`], one of the main macros of this crate, for more information. 4 | 5 | #![deny(unused_crate_dependencies)] 6 | pub mod internal; 7 | #[doc(hidden)] 8 | pub mod rel; 9 | pub mod aggregators; 10 | mod convert; 11 | mod rel_index_read; 12 | mod exps; 13 | #[cfg(feature = "par")] 14 | mod c_rel_index; 15 | #[cfg(feature = "par")] 16 | mod c_rel_index_read; 17 | #[cfg(feature = "par")] 18 | mod c_rel_full_index; 19 | #[cfg(feature = "par")] 20 | mod c_rel_index_combined; 21 | #[cfg(feature = "par")] 22 | mod c_lat_index; 23 | #[cfg(feature = "par")] 24 | mod c_rel_no_index; 25 | mod to_rel_index; 26 | mod tuple_of_borrowed; 27 | mod rel_index_boilerplate; 28 | 29 | pub use ascent_base::*; 30 | pub use ascent_macro::{ascent, ascent_run, ascent_source}; 31 | #[cfg(feature = "par")] 32 | pub use ascent_macro::{ascent_par, ascent_run_par}; 33 | #[cfg(feature = "par")] 34 | pub use dashmap; 35 | #[cfg(feature = "par")] 36 | pub use rayon; 37 | pub use {boxcar, hashbrown}; 38 | -------------------------------------------------------------------------------- /ascent/src/rel.rs: -------------------------------------------------------------------------------- 1 | //! The default data structure provider for Ascent relations 2 | 3 | macro_rules! _rel_type_template { 4 | ($field_types: ty, $indices: expr, $par: ident) => {}; 5 | } 6 | 7 | macro_rules! _rel_ind_template { 8 | ($field_types: ty, $indices: expr, $par: ident, $ind: expr) => {}; 9 | } 10 | 11 | #[doc(hidden)] 12 | #[macro_export] 13 | macro_rules! rel_codegen { 14 | ( $($tt: tt)* ) => { }; 15 | } 16 | pub use rel_codegen; 17 | 18 | #[doc(hidden)] 19 | #[macro_export] 20 | macro_rules! rel { 21 | ($name: ident, $field_types: ty, $indices: expr, ser, ()) => { 22 | ::std::vec::Vec<$field_types> 23 | }; 24 | ($name: ident, $field_types: ty, $indices: expr, par, ()) => { 25 | ::ascent::boxcar::Vec<$field_types> 26 | }; 27 | } 28 | pub use rel; 29 | 30 | #[doc(hidden)] 31 | #[macro_export] 32 | macro_rules! rel_ind_common { 33 | ($name: ident, $field_types: ty, $indices: expr, ser, ()) => { 34 | () 35 | }; 36 | ($name: ident, $field_types: ty, $indices: expr, par, ()) => { 37 | () 38 | }; 39 | } 40 | pub use rel_ind_common; 41 | 42 | #[doc(hidden)] 43 | #[macro_export] 44 | macro_rules! rel_full_ind { 45 | ($name: ident, $field_types: ty, $indices: expr, ser, (), $key: ty, $val: ty) => { 46 | ascent::internal::RelFullIndexType<$key, $val> 47 | }; 48 | ($name: ident, $field_types: ty, $indices: expr, par, (), $key: ty, $val: ty) => { 49 | ascent::internal::CRelFullIndex<$key, $val> 50 | }; 51 | } 52 | pub use rel_full_ind; 53 | 54 | #[doc(hidden)] 55 | #[macro_export] 56 | macro_rules! rel_ind { 57 | ($name: ident, $field_types: ty, $indices: expr, ser, (), $ind: expr, $key: ty, $val: ty) => { 58 | ascent::rel::ToRelIndexType<$key, $val> 59 | }; 60 | ($name: ident, $field_types: ty, $indices: expr, par, (), [], $key: ty, $val: ty) => { 61 | ascent::internal::CRelNoIndex<$val> 62 | }; 63 | ($name: ident, $field_types: ty, $indices: expr, par, (), $ind: expr, $key: ty, $val: ty) => { 64 | ascent::internal::CRelIndex<$key, $val> 65 | }; 66 | } 67 | pub use rel_ind; 68 | 69 | #[derive(Clone)] 70 | pub struct ToRelIndexType(pub RelIndexType1); 71 | 72 | impl Default for ToRelIndexType { 73 | #[inline(always)] 74 | fn default() -> Self { Self(Default::default()) } 75 | } 76 | 77 | impl ToRelIndex for ToRelIndexType { 78 | type RelIndex<'a> 79 | = &'a RelIndexType1 80 | where 81 | Self: 'a, 82 | R: 'a; 83 | 84 | #[inline(always)] 85 | fn to_rel_index<'a>(&'a self, _rel: &'a R) -> Self::RelIndex<'a> { &self.0 } 86 | 87 | type RelIndexWrite<'a> 88 | = &'a mut RelIndexType1 89 | where 90 | Self: 'a, 91 | R: 'a; 92 | 93 | #[inline(always)] 94 | fn to_rel_index_write<'a>(&'a mut self, _rel: &'a mut R) -> Self::RelIndexWrite<'a> { &mut self.0 } 95 | } 96 | 97 | use crate::internal::{Freezable, RelFullIndexType, RelIndexMerge, RelIndexType1}; 98 | use crate::to_rel_index::ToRelIndex; 99 | 100 | impl ToRelIndex for RelIndexType1 { 101 | type RelIndex<'a> 102 | = &'a Self 103 | where 104 | Self: 'a, 105 | Rel: 'a; 106 | #[inline(always)] 107 | fn to_rel_index<'a>(&'a self, _rel: &'a Rel) -> Self::RelIndex<'a> { self } 108 | 109 | type RelIndexWrite<'a> 110 | = &'a mut Self 111 | where 112 | Self: 'a, 113 | Rel: 'a; 114 | #[inline(always)] 115 | fn to_rel_index_write<'a>(&'a mut self, _rel: &'a mut Rel) -> Self::RelIndexWrite<'a> { self } 116 | } 117 | 118 | impl ToRelIndex for RelFullIndexType { 119 | type RelIndex<'a> 120 | = &'a Self 121 | where 122 | Self: 'a, 123 | Rel: 'a; 124 | #[inline(always)] 125 | fn to_rel_index<'a>(&'a self, _rel: &'a Rel) -> Self::RelIndex<'a> { self } 126 | 127 | type RelIndexWrite<'a> 128 | = &'a mut Self 129 | where 130 | Self: 'a, 131 | Rel: 'a; 132 | #[inline(always)] 133 | fn to_rel_index_write<'a>(&'a mut self, _rel: &'a mut Rel) -> Self::RelIndexWrite<'a> { self } 134 | } 135 | 136 | #[cfg(feature = "par")] 137 | mod par { 138 | use crate::c_rel_full_index::CRelFullIndex; 139 | use crate::c_rel_index::CRelIndex; 140 | use crate::c_rel_no_index::CRelNoIndex; 141 | use crate::to_rel_index::ToRelIndex; 142 | 143 | impl ToRelIndex for CRelIndex { 144 | type RelIndex<'a> 145 | = &'a Self 146 | where 147 | Self: 'a, 148 | Rel: 'a; 149 | #[inline(always)] 150 | fn to_rel_index<'a>(&'a self, _rel: &'a Rel) -> Self::RelIndex<'a> { self } 151 | 152 | type RelIndexWrite<'a> 153 | = &'a mut Self 154 | where 155 | Self: 'a, 156 | Rel: 'a; 157 | #[inline(always)] 158 | fn to_rel_index_write<'a>(&'a mut self, _rel: &'a mut Rel) -> Self::RelIndexWrite<'a> { self } 159 | } 160 | 161 | impl ToRelIndex for CRelNoIndex { 162 | type RelIndex<'a> 163 | = &'a Self 164 | where 165 | Self: 'a, 166 | Rel: 'a; 167 | #[inline(always)] 168 | fn to_rel_index<'a>(&'a self, _rel: &'a Rel) -> Self::RelIndex<'a> { self } 169 | 170 | type RelIndexWrite<'a> 171 | = &'a mut Self 172 | where 173 | Self: 'a, 174 | Rel: 'a; 175 | #[inline(always)] 176 | fn to_rel_index_write<'a>(&'a mut self, _rel: &'a mut Rel) -> Self::RelIndexWrite<'a> { self } 177 | } 178 | 179 | impl ToRelIndex for CRelFullIndex { 180 | type RelIndex<'a> 181 | = &'a Self 182 | where 183 | Self: 'a, 184 | Rel: 'a; 185 | #[inline(always)] 186 | fn to_rel_index<'a>(&'a self, _rel: &'a Rel) -> Self::RelIndex<'a> { self } 187 | 188 | type RelIndexWrite<'a> 189 | = &'a mut Self 190 | where 191 | Self: 'a, 192 | Rel: 'a; 193 | #[inline(always)] 194 | fn to_rel_index_write<'a>(&'a mut self, _rel: &'a mut Rel) -> Self::RelIndexWrite<'a> { self } 195 | } 196 | } 197 | 198 | impl RelIndexMerge for () { 199 | #[inline(always)] 200 | fn move_index_contents(_from: &mut Self, _to: &mut Self) {} 201 | 202 | #[inline(always)] 203 | fn merge_delta_to_total_new_to_delta(_new: &mut Self, _delta: &mut Self, _total: &mut Self) {} 204 | } 205 | 206 | impl Freezable for () { 207 | fn freeze(&mut self) {} 208 | fn unfreeze(&mut self) {} 209 | } 210 | -------------------------------------------------------------------------------- /ascent/src/rel_index_boilerplate.rs: -------------------------------------------------------------------------------- 1 | use crate::internal::{ 2 | CRelFullIndexWrite, CRelIndexWrite, RelFullIndexRead, RelFullIndexWrite, RelIndexMerge, RelIndexWrite, 3 | }; 4 | use crate::rel_index_read::{RelIndexRead, RelIndexReadAll}; 5 | 6 | impl RelIndexWrite for &mut T 7 | where T: RelIndexWrite 8 | { 9 | type Key = T::Key; 10 | type Value = T::Value; 11 | 12 | #[inline(always)] 13 | fn index_insert(&mut self, key: Self::Key, value: Self::Value) { (**self).index_insert(key, value) } 14 | } 15 | 16 | impl RelFullIndexWrite for &mut T 17 | where T: RelFullIndexWrite 18 | { 19 | type Key = T::Key; 20 | type Value = T::Value; 21 | 22 | #[inline(always)] 23 | fn insert_if_not_present(&mut self, key: &Self::Key, v: Self::Value) -> bool { 24 | (**self).insert_if_not_present(key, v) 25 | } 26 | } 27 | 28 | impl CRelIndexWrite for &T 29 | where T: CRelIndexWrite 30 | { 31 | type Key = T::Key; 32 | type Value = T::Value; 33 | 34 | #[inline(always)] 35 | fn index_insert(&self, key: Self::Key, value: Self::Value) { (**self).index_insert(key, value) } 36 | } 37 | 38 | impl CRelFullIndexWrite for &T 39 | where T: CRelFullIndexWrite 40 | { 41 | type Key = T::Key; 42 | type Value = T::Value; 43 | 44 | #[inline(always)] 45 | fn insert_if_not_present(&self, key: &Self::Key, v: Self::Value) -> bool { (**self).insert_if_not_present(key, v) } 46 | } 47 | 48 | impl RelIndexMerge for &mut T 49 | where T: RelIndexMerge 50 | { 51 | fn move_index_contents(from: &mut Self, to: &mut Self) { T::move_index_contents(*from, *to) } 52 | 53 | fn merge_delta_to_total_new_to_delta(new: &mut Self, delta: &mut Self, total: &mut Self) { 54 | T::merge_delta_to_total_new_to_delta(*new, *delta, *total) 55 | } 56 | 57 | fn init(new: &mut Self, delta: &mut Self, total: &mut Self) { T::init(new, delta, total) } 58 | } 59 | 60 | impl<'a, T> RelIndexRead<'a> for &'a T 61 | where T: RelIndexRead<'a> 62 | { 63 | type Key = T::Key; 64 | type Value = T::Value; 65 | type IteratorType = T::IteratorType; 66 | 67 | #[inline(always)] 68 | fn index_get(&'a self, key: &Self::Key) -> Option { (**self).index_get(key) } 69 | 70 | #[inline(always)] 71 | fn len_estimate(&self) -> usize { (**self).len_estimate() } 72 | 73 | #[inline(always)] 74 | fn is_empty(&'a self) -> bool { (**self).is_empty() } 75 | } 76 | 77 | impl<'a, T> RelIndexReadAll<'a> for &'a T 78 | where T: RelIndexReadAll<'a> 79 | { 80 | type Key = T::Key; 81 | type Value = T::Value; 82 | type ValueIteratorType = T::ValueIteratorType; 83 | type AllIteratorType = T::AllIteratorType; 84 | 85 | #[inline(always)] 86 | fn iter_all(&'a self) -> Self::AllIteratorType { (**self).iter_all() } 87 | } 88 | 89 | impl<'a, T> RelFullIndexRead<'a> for &'a T 90 | where T: RelFullIndexRead<'a> 91 | { 92 | type Key = T::Key; 93 | #[inline(always)] 94 | fn contains_key(&self, key: &Self::Key) -> bool { (**self).contains_key(key) } 95 | } 96 | 97 | #[cfg(feature = "par")] 98 | mod par { 99 | use crate::internal::{CRelIndexRead, CRelIndexReadAll}; 100 | 101 | impl<'a, T> CRelIndexRead<'a> for &'a T 102 | where T: CRelIndexRead<'a> 103 | { 104 | type Key = T::Key; 105 | type Value = T::Value; 106 | type IteratorType = T::IteratorType; 107 | 108 | #[inline(always)] 109 | fn c_index_get(&'a self, key: &Self::Key) -> Option { (**self).c_index_get(key) } 110 | } 111 | 112 | impl<'a, T> CRelIndexReadAll<'a> for &'a T 113 | where T: CRelIndexReadAll<'a> 114 | { 115 | type Key = T::Key; 116 | type Value = T::Value; 117 | type ValueIteratorType = T::ValueIteratorType; 118 | type AllIteratorType = T::AllIteratorType; 119 | 120 | #[inline(always)] 121 | fn c_iter_all(&'a self) -> Self::AllIteratorType { (**self).c_iter_all() } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /ascent/src/to_rel_index.rs: -------------------------------------------------------------------------------- 1 | pub trait ToRelIndex0 { 2 | type RelIndex<'a> 3 | where 4 | Self: 'a, 5 | Rel: 'a; 6 | fn to_rel_index<'a>(&'a self, rel: &'a Rel) -> Self::RelIndex<'a>; 7 | 8 | type RelIndexWrite<'a> 9 | where 10 | Self: 'a, 11 | Rel: 'a; 12 | fn to_rel_index_write<'a>(&'a mut self, rel: &'a mut Rel) -> Self::RelIndexWrite<'a>; 13 | 14 | type CRelIndexWrite<'a> 15 | where 16 | Self: 'a, 17 | Rel: 'a; 18 | fn to_c_rel_index_write<'a>(&'a self, rel: &'a Rel) -> Self::CRelIndexWrite<'a>; 19 | } 20 | 21 | pub trait ToRelIndex { 22 | type RelIndex<'a> 23 | where 24 | Self: 'a, 25 | Rel: 'a; 26 | fn to_rel_index<'a>(&'a self, rel: &'a Rel) -> Self::RelIndex<'a>; 27 | 28 | type RelIndexWrite<'a> 29 | where 30 | Self: 'a, 31 | Rel: 'a; 32 | fn to_rel_index_write<'a>(&'a mut self, rel: &'a mut Rel) -> Self::RelIndexWrite<'a>; 33 | } 34 | 35 | impl ToRelIndex0 for T 36 | where T: ToRelIndex 37 | { 38 | type RelIndex<'a> 39 | = T::RelIndex<'a> 40 | where 41 | Self: 'a, 42 | Rel: 'a; 43 | 44 | #[inline(always)] 45 | fn to_rel_index<'a>(&'a self, rel: &'a Rel) -> Self::RelIndex<'a> { self.to_rel_index(rel) } 46 | 47 | type RelIndexWrite<'a> 48 | = T::RelIndexWrite<'a> 49 | where 50 | Self: 'a, 51 | Rel: 'a; 52 | #[inline(always)] 53 | fn to_rel_index_write<'a>(&'a mut self, rel: &'a mut Rel) -> Self::RelIndexWrite<'a> { self.to_rel_index_write(rel) } 54 | 55 | type CRelIndexWrite<'a> 56 | = T::RelIndex<'a> 57 | where 58 | Self: 'a, 59 | Rel: 'a; 60 | #[inline(always)] 61 | fn to_c_rel_index_write<'a>(&'a self, rel: &'a Rel) -> Self::CRelIndexWrite<'a> { self.to_rel_index(rel) } 62 | } 63 | -------------------------------------------------------------------------------- /ascent/src/tuple_of_borrowed.rs: -------------------------------------------------------------------------------- 1 | use paste::paste; 2 | 3 | pub trait TupleOfBorrowed { 4 | type Tuple; 5 | fn tuple_of_borrowed(self) -> Self::Tuple; 6 | } 7 | 8 | impl<'a, T1> TupleOfBorrowed for &'a (T1,) { 9 | type Tuple = (&'a T1,); 10 | 11 | #[inline(always)] 12 | fn tuple_of_borrowed(self) -> Self::Tuple { (&self.0,) } 13 | } 14 | 15 | impl TupleOfBorrowed for (&T1,) { 16 | type Tuple = Self; 17 | 18 | #[inline(always)] 19 | fn tuple_of_borrowed(self) -> Self::Tuple { self } 20 | } 21 | 22 | macro_rules! impl_tuple_of_borrowed { 23 | ($($i: literal),*) => { paste!{ 24 | impl<'a, $([]),*> TupleOfBorrowed for &'a ($([]),*) { 25 | type Tuple = ($(&'a []),*); 26 | #[allow(clippy::unused_unit)] 27 | #[inline(always)] 28 | fn tuple_of_borrowed(self) -> Self::Tuple { 29 | ($(&self.$i),*) 30 | } 31 | } 32 | 33 | impl<'a, $([]),*> TupleOfBorrowed for ($(&'a []),*) { 34 | type Tuple = Self; 35 | #[inline(always)] 36 | fn tuple_of_borrowed(self) -> Self::Tuple { 37 | self 38 | } 39 | } 40 | }}; 41 | } 42 | 43 | impl_tuple_of_borrowed!(); 44 | // impl_tuple_of_borrowed!(0); 45 | impl_tuple_of_borrowed!(0, 1); 46 | impl_tuple_of_borrowed!(0, 1, 2); 47 | impl_tuple_of_borrowed!(0, 1, 2, 3); 48 | impl_tuple_of_borrowed!(0, 1, 2, 3, 4); 49 | impl_tuple_of_borrowed!(0, 1, 2, 3, 4, 5); 50 | impl_tuple_of_borrowed!(0, 1, 2, 3, 4, 5, 6); 51 | impl_tuple_of_borrowed!(0, 1, 2, 3, 4, 5, 6, 7); 52 | impl_tuple_of_borrowed!(0, 1, 2, 3, 4, 5, 6, 7, 8); 53 | impl_tuple_of_borrowed!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); 54 | impl_tuple_of_borrowed!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); 55 | impl_tuple_of_borrowed!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11); 56 | -------------------------------------------------------------------------------- /ascent_base/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ascent_base" 3 | version.workspace = true 4 | edition = "2021" 5 | authors = ["Arash Sahebolamri"] 6 | repository = "https://github.com/s-arash/ascent" 7 | license = "MIT" 8 | description = "support library for the ascent package" 9 | 10 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 11 | 12 | [dependencies] 13 | paste = "1.0" 14 | -------------------------------------------------------------------------------- /ascent_base/src/lattice.rs: -------------------------------------------------------------------------------- 1 | //! Defines the `Lattice` trait and provides implementations for standard types 2 | 3 | pub mod constant_propagation; 4 | pub mod set; 5 | pub mod product; 6 | pub mod ord_lattice; 7 | pub mod bounded_set; 8 | pub use product::Product; 9 | pub mod tuple; 10 | use std::cmp::{Ordering, Reverse}; 11 | use std::rc::Rc; 12 | use std::sync::Arc; 13 | mod dual; 14 | pub use dual::Dual; 15 | 16 | /// A `Lattice` is a `PartialOrd` where each pair of elements has a least upper bound (`join`) and a greatest lower bound (`meet`) 17 | pub trait Lattice: PartialOrd + Sized { 18 | /// ensures `self` is the join of `self` and `other` 19 | /// 20 | /// Returns true if `self` was changed. 21 | fn meet_mut(&mut self, other: Self) -> bool; 22 | 23 | /// ensures `self` is the meet of `self` and `other`. 24 | /// 25 | /// Returns true if `self` was changed. 26 | fn join_mut(&mut self, other: Self) -> bool; 27 | 28 | /// The greatest lower bound of two elements. `meet(x, y)` is the biggest value `z` 29 | /// s.t. `z <= x` and `z <= y` 30 | fn meet(mut self, other: Self) -> Self { 31 | self.meet_mut(other); 32 | self 33 | } 34 | 35 | /// The least upper bound of two elements. `join(x, y)` is the smallest value `z` 36 | /// s.t. `z >= x` and `z >= y`. 37 | fn join(mut self, other: Self) -> Self { 38 | self.join_mut(other); 39 | self 40 | } 41 | } 42 | 43 | pub trait BoundedLattice: Lattice { 44 | fn bottom() -> Self; 45 | fn top() -> Self; 46 | } 47 | 48 | macro_rules! ord_lattice_impl { 49 | ($t: ty) => { 50 | impl Lattice for $t { 51 | fn meet_mut(&mut self, other: Self) -> bool { 52 | #[allow(clippy::neg_cmp_op_on_partial_ord)] 53 | let changed = !(*self <= other); 54 | if changed { 55 | *self = other; 56 | } 57 | changed 58 | } 59 | 60 | fn join_mut(&mut self, other: Self) -> bool { 61 | #[allow(clippy::neg_cmp_op_on_partial_ord)] 62 | let changed = !(*self >= other); 63 | if changed { 64 | *self = other; 65 | } 66 | changed 67 | } 68 | } 69 | }; 70 | } 71 | 72 | ord_lattice_impl!(bool); 73 | 74 | impl BoundedLattice for bool { 75 | #[inline] 76 | fn bottom() -> Self { false } 77 | #[inline] 78 | fn top() -> Self { true } 79 | } 80 | 81 | macro_rules! num_lattice_impl { 82 | ($int:ty) => { 83 | ord_lattice_impl!($int); 84 | impl BoundedLattice for $int { 85 | fn bottom() -> Self { Self::MIN } 86 | fn top() -> Self { Self::MAX } 87 | } 88 | }; 89 | } 90 | 91 | num_lattice_impl!(i8); 92 | num_lattice_impl!(u8); 93 | num_lattice_impl!(i16); 94 | num_lattice_impl!(u16); 95 | num_lattice_impl!(i32); 96 | num_lattice_impl!(u32); 97 | num_lattice_impl!(i64); 98 | num_lattice_impl!(u64); 99 | num_lattice_impl!(i128); 100 | num_lattice_impl!(u128); 101 | 102 | num_lattice_impl!(isize); 103 | num_lattice_impl!(usize); 104 | 105 | impl Lattice for Option { 106 | fn meet_mut(&mut self, other: Self) -> bool { 107 | match (self, other) { 108 | (Some(x), Some(y)) => x.meet_mut(y), 109 | (this @ Some(_), None) => { 110 | *this = None; 111 | true 112 | }, 113 | (None, _) => false, 114 | } 115 | } 116 | 117 | fn join_mut(&mut self, other: Self) -> bool { 118 | match (self, other) { 119 | (Some(x), Some(y)) => x.join_mut(y), 120 | (this @ None, Some(y)) => { 121 | *this = Some(y); 122 | true 123 | }, 124 | (_, None) => false, 125 | } 126 | } 127 | } 128 | 129 | impl BoundedLattice for Option { 130 | #[inline] 131 | fn bottom() -> Self { None } 132 | #[inline] 133 | fn top() -> Self { Some(T::top()) } 134 | } 135 | 136 | impl Lattice for Rc { 137 | fn meet_mut(&mut self, other: Self) -> bool { 138 | match self.as_ref().partial_cmp(&other) { 139 | Some(Ordering::Less | Ordering::Equal) => false, 140 | Some(Ordering::Greater) => { 141 | *self = other; 142 | true 143 | }, 144 | // Stable in 1.76: 145 | // None => Rc::make_mut(self).meet_mut(Rc::unwrap_or_clone(other)) 146 | None => Rc::make_mut(self).meet_mut(Rc::try_unwrap(other).unwrap_or_else(|rc| (*rc).clone())), 147 | } 148 | } 149 | 150 | fn join_mut(&mut self, other: Self) -> bool { 151 | match self.as_ref().partial_cmp(&other) { 152 | Some(Ordering::Greater | Ordering::Equal) => false, 153 | Some(Ordering::Less) => { 154 | *self = other; 155 | true 156 | }, 157 | // Stable in 1.76: 158 | // None => Rc::make_mut(self).join_mut(Rc::unwrap_or_clone(other)) 159 | None => Rc::make_mut(self).join_mut(Rc::try_unwrap(other).unwrap_or_else(|rc| (*rc).clone())), 160 | } 161 | } 162 | } 163 | 164 | impl Lattice for Arc { 165 | fn meet_mut(&mut self, other: Self) -> bool { 166 | match self.as_ref().partial_cmp(&other) { 167 | Some(Ordering::Less | Ordering::Equal) => false, 168 | Some(Ordering::Greater) => { 169 | *self = other; 170 | true 171 | }, 172 | // Stable in 1.76: 173 | // None => Arc::make_mut(self).meet_mut(Arc::unwrap_or_clone(other)) 174 | None => Arc::make_mut(self).meet_mut(Arc::try_unwrap(other).unwrap_or_else(|rc| (*rc).clone())), 175 | } 176 | } 177 | 178 | fn join_mut(&mut self, other: Self) -> bool { 179 | match self.as_ref().partial_cmp(&other) { 180 | Some(Ordering::Greater | Ordering::Equal) => false, 181 | Some(Ordering::Less) => { 182 | *self = other; 183 | true 184 | }, 185 | // Stable in 1.76: 186 | // None => Arc::make_mut(self).join_mut(Arc::unwrap_or_clone(other)) 187 | None => Arc::make_mut(self).join_mut(Arc::try_unwrap(other).unwrap_or_else(|rc| (*rc).clone())), 188 | } 189 | } 190 | } 191 | 192 | impl Lattice for Box { 193 | fn meet_mut(&mut self, other: Self) -> bool { self.as_mut().meet_mut(*other) } 194 | 195 | fn join_mut(&mut self, other: Self) -> bool { self.as_mut().join_mut(*other) } 196 | } 197 | 198 | impl Lattice for Reverse { 199 | #[inline] 200 | fn meet(self, other: Self) -> Self { Reverse(self.0.join(other.0)) } 201 | 202 | #[inline] 203 | fn join(self, other: Self) -> Self { Reverse(self.0.meet(other.0)) } 204 | 205 | #[inline] 206 | fn meet_mut(&mut self, other: Self) -> bool { self.0.join_mut(other.0) } 207 | 208 | #[inline] 209 | fn join_mut(&mut self, other: Self) -> bool { self.0.meet_mut(other.0) } 210 | } 211 | 212 | impl BoundedLattice for Reverse { 213 | #[inline] 214 | fn bottom() -> Self { Reverse(T::top()) } 215 | 216 | #[inline] 217 | fn top() -> Self { Reverse(T::bottom()) } 218 | } 219 | 220 | #[cfg(test)] 221 | mod tests { 222 | use std::sync::Arc; 223 | 224 | use crate::Lattice; 225 | 226 | #[test] 227 | fn test_arc_lattice() { 228 | let x = Arc::new(42); 229 | let y = Arc::new(17); 230 | assert_eq!(*x.clone().meet(y.clone()), 17); 231 | assert_eq!(*x.meet(y), 17); 232 | 233 | let x = Arc::new(42); 234 | let y = Arc::new(17); 235 | assert_eq!(*x.clone().join(y.clone()), 42); 236 | assert_eq!(*x.join(y), 42); 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /ascent_base/src/lattice/bounded_set.rs: -------------------------------------------------------------------------------- 1 | use std::hash::Hash; 2 | 3 | use super::BoundedLattice; 4 | use super::set::Set; 5 | use crate::Lattice; 6 | 7 | /// `BoundedSet` is a generalization of the flat lattice. 8 | /// 9 | /// A `BoundedSet` stores at most `BOUND` items, and if asked to store more, will go to `TOP`. 10 | #[derive(Clone, PartialEq, Eq, Hash, Debug)] 11 | pub struct BoundedSet(Option>); 12 | 13 | impl Default for BoundedSet { 14 | fn default() -> Self { Self::new() } 15 | } 16 | 17 | impl BoundedSet { 18 | /// A set containing everything 19 | pub const TOP: Self = BoundedSet(None); 20 | 21 | /// Creates an empty `BoundedSet` 22 | pub fn new() -> Self { BoundedSet(Some(Set::default())) } 23 | 24 | /// Creates a `BoundedSet` containing only `item` 25 | pub fn singleton(item: T) -> Self { Self::from_set(Set::singleton(item)) } 26 | 27 | /// Creates a `BoundedSet` from a `Set`, ensuring the `BOUND` is not exceeded 28 | pub fn from_set(set: Set) -> Self { if set.len() <= BOUND { BoundedSet(Some(set)) } else { BoundedSet(None) } } 29 | 30 | /// Returns the size of the set. In case of the set being `TOP`, returns `None` 31 | pub fn count(&self) -> Option { self.0.as_ref().map(|s| s.len()) } 32 | 33 | /// Returns `true` if the set contains the `item`. For a set that `is_top()`, always returns `true`. 34 | pub fn contains(&self, item: &T) -> bool { 35 | match &self.0 { 36 | Some(set) => set.0.contains(item), 37 | None => true, 38 | } 39 | } 40 | 41 | pub fn is_top(&self) -> bool { self.0.is_none() } 42 | } 43 | 44 | impl PartialOrd for BoundedSet { 45 | fn partial_cmp(&self, other: &Self) -> Option { 46 | use std::cmp::Ordering; 47 | match (&self.0, &other.0) { 48 | (None, None) => Some(Ordering::Equal), 49 | (None, _) => Some(Ordering::Greater), 50 | (_, None) => Some(Ordering::Less), 51 | (Some(set1), Some(set2)) => set1.partial_cmp(set2), 52 | } 53 | } 54 | } 55 | 56 | impl Lattice for BoundedSet { 57 | fn meet_mut(&mut self, other: Self) -> bool { 58 | match (&mut self.0, other.0) { 59 | (None, None) => false, 60 | (this @ None, Some(set2)) => { 61 | *this = Some(set2); 62 | true 63 | }, 64 | (Some(_), None) => false, 65 | (Some(set1), Some(set2)) => set1.meet_mut(set2), 66 | } 67 | } 68 | 69 | fn join_mut(&mut self, other: Self) -> bool { 70 | match (&mut self.0, other.0) { 71 | (None, _) => false, 72 | (this @ Some(_), None) => { 73 | *this = None; 74 | true 75 | }, 76 | (Some(set1), Some(set2)) => { 77 | let changed = set1.join_mut(set2); 78 | if set1.len() > BOUND { 79 | self.0 = None; 80 | true 81 | } else { 82 | changed 83 | } 84 | }, 85 | } 86 | } 87 | fn meet(self, other: Self) -> Self { 88 | match (self.0, other.0) { 89 | (None, None) => BoundedSet(None), 90 | (None, set2 @ Some(_)) => BoundedSet(set2), 91 | (set1 @ Some(_), None) => BoundedSet(set1), 92 | (Some(set1), Some(set2)) => { 93 | let res = set1.meet(set2); 94 | BoundedSet(Some(res)) 95 | }, 96 | } 97 | } 98 | 99 | fn join(self, other: Self) -> Self { 100 | match (self.0, other.0) { 101 | (None, _) => BoundedSet(None), 102 | (_, None) => BoundedSet(None), 103 | (Some(set1), Some(set2)) => { 104 | let res = set1.join(set2); 105 | if res.len() > BOUND { BoundedSet(None) } else { BoundedSet(Some(res)) } 106 | }, 107 | } 108 | } 109 | } 110 | 111 | impl BoundedLattice for BoundedSet { 112 | fn bottom() -> Self { Self::new() } 113 | 114 | /// top is meant to represent a set containing everything 115 | fn top() -> Self { BoundedSet(None) } 116 | } 117 | 118 | #[test] 119 | fn test_bounded_set() { 120 | let set1 = BoundedSet::<2, i32>::singleton(10); 121 | 122 | let mut set2_by_mut = set1.clone(); 123 | assert!(set2_by_mut.join_mut(BoundedSet::singleton(11))); 124 | 125 | let set2 = set1.join(BoundedSet::singleton(11)); 126 | 127 | assert!(set2_by_mut == set2); 128 | assert_eq!(set2.count(), Some(2)); 129 | assert!(set2.contains(&10)); 130 | assert!(!set2.contains(&20)); 131 | 132 | let set3 = set2.join(BoundedSet::singleton(12)); 133 | assert!(set3.is_top()); 134 | assert!(set3 == BoundedSet::TOP); 135 | assert!(set3.contains(&15)); 136 | } 137 | -------------------------------------------------------------------------------- /ascent_base/src/lattice/constant_propagation.rs: -------------------------------------------------------------------------------- 1 | use std::cmp::Ordering; 2 | 3 | use super::{BoundedLattice, Lattice}; 4 | 5 | /// A flat `Lattice`: `Bottom` <= everything <= `Top`, and `Constant(x) == Constant(y)` iff `x == y` 6 | #[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)] 7 | #[allow(dead_code)] 8 | pub enum ConstPropagation { 9 | Bottom, 10 | Constant(T), 11 | Top, 12 | } 13 | 14 | impl PartialOrd for ConstPropagation { 15 | fn partial_cmp(&self, other: &Self) -> Option { 16 | use ConstPropagation::*; 17 | match (self, other) { 18 | (Bottom, Bottom) => Some(Ordering::Equal), 19 | (Bottom, _) => Some(Ordering::Less), 20 | (Constant(_x), Bottom) => Some(Ordering::Greater), 21 | (Constant(x), Constant(y)) => 22 | if x == y { 23 | Some(Ordering::Equal) 24 | } else { 25 | None 26 | }, 27 | (Constant(_), Top) => Some(Ordering::Less), 28 | (Top, Top) => Some(Ordering::Equal), 29 | (Top, _) => Some(Ordering::Greater), 30 | } 31 | } 32 | } 33 | 34 | impl Lattice for ConstPropagation { 35 | fn meet(self, other: Self) -> Self { 36 | use ConstPropagation::*; 37 | match (self, other) { 38 | (Bottom, _) => Self::Bottom, 39 | (Constant(_x), Bottom) => Self::Bottom, 40 | (Constant(x), Constant(y)) => 41 | if x == y { 42 | Constant(x) 43 | } else { 44 | Self::Bottom 45 | }, 46 | (Constant(x), Top) => Constant(x), 47 | (Top, other) => other, 48 | } 49 | } 50 | 51 | fn join(self, other: Self) -> Self { 52 | use ConstPropagation::*; 53 | match (self, other) { 54 | (Bottom, other) => other, 55 | (Constant(x), Bottom) => Constant(x), 56 | (Constant(x), Constant(y)) => 57 | if x == y { 58 | Constant(x) 59 | } else { 60 | Self::Top 61 | }, 62 | (Constant(_x), Top) => Top, 63 | (Top, _) => Top, 64 | } 65 | } 66 | 67 | fn meet_mut(&mut self, other: Self) -> bool { 68 | use ConstPropagation::*; 69 | match (self, other) { 70 | (Bottom, _) => false, 71 | (Constant(x), Constant(y)) if x == &y => false, 72 | (this @ Constant(_), Bottom | Constant(_)) => { 73 | *this = Bottom; 74 | true 75 | }, 76 | (_, Top) => false, 77 | (this @ Top, other) => { 78 | *this = other; 79 | true 80 | }, 81 | } 82 | } 83 | 84 | fn join_mut(&mut self, other: Self) -> bool { 85 | use ConstPropagation::*; 86 | match (self, other) { 87 | (_, Bottom) => false, 88 | (this @ Bottom, other) => { 89 | *this = other; 90 | true 91 | }, 92 | (Constant(x), Constant(y)) if x == &y => false, 93 | (this @ Constant(_), Constant(_) | Top) => { 94 | *this = Top; 95 | true 96 | }, 97 | (Top, _) => false, 98 | } 99 | } 100 | } 101 | 102 | impl BoundedLattice for ConstPropagation 103 | where ConstPropagation: Lattice 104 | { 105 | fn top() -> Self { Self::Top } 106 | fn bottom() -> Self { Self::Bottom } 107 | } 108 | 109 | #[test] 110 | fn test_constant_propagation() { 111 | let const_1 = ConstPropagation::Constant(1); 112 | assert!(const_1 > ConstPropagation::Bottom); 113 | assert!(const_1 < ConstPropagation::Top); 114 | assert!(const_1 > ConstPropagation::bottom()); 115 | } 116 | 117 | #[test] 118 | fn test_constant_propagation_lattice() { 119 | let const_1 = ConstPropagation::Constant(1); 120 | 121 | let mut x = const_1; 122 | assert!(!x.join_mut(const_1)); 123 | assert!(!x.meet_mut(const_1)); 124 | assert!(!x.join_mut(ConstPropagation::Bottom)); 125 | assert!(!x.meet_mut(ConstPropagation::Top)); 126 | 127 | assert!(x == const_1); 128 | 129 | assert!(x.join_mut(ConstPropagation::Constant(2))); 130 | 131 | assert_eq!(x, ConstPropagation::Top); 132 | 133 | assert!(!x.join_mut(ConstPropagation::Constant(2))); 134 | } 135 | -------------------------------------------------------------------------------- /ascent_base/src/lattice/dual.rs: -------------------------------------------------------------------------------- 1 | use std::cmp::Ordering; 2 | use std::fmt::{Debug, Display, Formatter}; 3 | use std::ops::Deref; 4 | 5 | use super::BoundedLattice; 6 | use crate::Lattice; 7 | 8 | #[derive(PartialEq, Eq, Clone, Copy, Hash)] 9 | // TODO uncomment for a major release 10 | // #[repr(transparent)] 11 | /// A wrapper type that swaps (`<=` and `>=`) for `PartialOrd`s, (`meet` and `join`) for `Lattice`s, 12 | /// and (`top` and `bottom`) for `BoundedLattice`s. 13 | /// 14 | /// # Example 15 | /// ``` 16 | /// # use ascent_base::lattice::Dual; 17 | /// assert!(Dual(2) < Dual(1)); 18 | /// ``` 19 | pub struct Dual(pub T); 20 | 21 | impl Deref for Dual { 22 | type Target = T; 23 | fn deref(&self) -> &Self::Target { &self.0 } 24 | } 25 | 26 | impl Debug for Dual { 27 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { self.0.fmt(f) } 28 | } 29 | 30 | impl Display for Dual { 31 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { self.0.fmt(f) } 32 | } 33 | 34 | impl PartialOrd for Dual 35 | where T: PartialOrd 36 | { 37 | fn partial_cmp(&self, other: &Self) -> Option { other.0.partial_cmp(&self.0) } 38 | } 39 | 40 | impl Ord for Dual 41 | where T: Ord 42 | { 43 | fn cmp(&self, other: &Self) -> Ordering { other.0.cmp(&self.0) } 44 | } 45 | 46 | impl Lattice for Dual { 47 | #[inline] 48 | fn meet(self, other: Self) -> Self { Dual(self.0.join(other.0)) } 49 | 50 | #[inline] 51 | fn join(self, other: Self) -> Self { Dual(self.0.meet(other.0)) } 52 | 53 | #[inline] 54 | fn meet_mut(&mut self, other: Self) -> bool { self.0.join_mut(other.0) } 55 | 56 | #[inline] 57 | fn join_mut(&mut self, other: Self) -> bool { self.0.meet_mut(other.0) } 58 | } 59 | 60 | impl BoundedLattice for Dual { 61 | #[inline] 62 | fn top() -> Self { Dual(T::bottom()) } 63 | 64 | #[inline] 65 | fn bottom() -> Self { Dual(T::top()) } 66 | } 67 | -------------------------------------------------------------------------------- /ascent_base/src/lattice/ord_lattice.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{Debug, Formatter}; 2 | 3 | use crate::Lattice; 4 | 5 | #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 6 | pub struct OrdLattice(pub T); 7 | 8 | impl Debug for OrdLattice { 9 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { self.0.fmt(f) } 10 | } 11 | 12 | impl Lattice for OrdLattice { 13 | #[inline(always)] 14 | fn meet(self, other: Self) -> Self { self.min(other) } 15 | 16 | #[inline(always)] 17 | fn join(self, other: Self) -> Self { self.max(other) } 18 | 19 | fn meet_mut(&mut self, other: Self) -> bool { 20 | if self.0 > other.0 { 21 | self.0 = other.0; 22 | true 23 | } else { 24 | false 25 | } 26 | } 27 | fn join_mut(&mut self, other: Self) -> bool { 28 | if self.0 < other.0 { 29 | self.0 = other.0; 30 | true 31 | } else { 32 | false 33 | } 34 | } 35 | } 36 | 37 | #[test] 38 | fn test_ord_lattice() { 39 | assert_eq!(OrdLattice(42).meet(OrdLattice(22)), OrdLattice(22)); 40 | 41 | let mut x = OrdLattice(42); 42 | assert!(!x.join_mut(OrdLattice(42))); 43 | assert_eq!(x.0, 42); 44 | assert!(!x.meet_mut(OrdLattice(42))); 45 | assert_eq!(x.0, 42); 46 | } 47 | -------------------------------------------------------------------------------- /ascent_base/src/lattice/product.rs: -------------------------------------------------------------------------------- 1 | use std::cmp::Ordering; 2 | 3 | use paste::paste; 4 | 5 | use super::{BoundedLattice, Lattice}; 6 | 7 | #[derive(Clone, Copy, PartialEq, Eq, Debug)] 8 | /// A wrapper for tuple types and arrays that implements `PartialOrd` using 9 | /// [product-order](https://en.wikipedia.org/wiki/Product_order) semantics. 10 | /// 11 | /// `Lattice` and `BoundedLattice` traits are also implemented. 12 | /// 13 | /// Difference from lexicographical ordering (the `PartialOrd` implementation for tuple types): 14 | /// ``` 15 | /// # use ascent_base::lattice::Product; 16 | /// assert!(!{Product((1,4)) < Product((2,3))}); 17 | /// assert!((1,4) < (2,3)); 18 | /// 19 | /// ``` 20 | pub struct Product(pub T); 21 | 22 | #[inline] 23 | fn combine_orderings(ord1: Ordering, ord2: Ordering) -> Option { 24 | use Ordering::*; 25 | match (ord1, ord2) { 26 | (Equal, _) => Some(ord2), 27 | (_, Equal) => Some(ord1), 28 | (Less, Less) => Some(Less), 29 | (Greater, Greater) => Some(Greater), 30 | _ => None, 31 | } 32 | } 33 | 34 | macro_rules! tuple_lattice_impl { 35 | ($($i:tt),*) => { paste!( 36 | impl< $([]: PartialOrd),* > PartialOrd for Product<($([]),*,)> { 37 | fn partial_cmp(&self, other: &Self) -> Option { 38 | let mut res = Ordering::Equal; 39 | $( 40 | match self.0.$i.partial_cmp(&other.0.$i) { 41 | None => return None, 42 | Some(ord) => { 43 | match combine_orderings(ord, res) { 44 | None => return None, 45 | Some(new_res) => res = new_res, 46 | } 47 | } 48 | }; 49 | )* 50 | Some(res) 51 | } 52 | } 53 | impl< $([]: Lattice),* > Lattice for Product<($([]),*,)> { 54 | fn meet_mut(&mut self, other: Self) -> bool { 55 | let mut changed = false; 56 | $(changed |= self.0.$i.meet_mut(other.0.$i);)* 57 | changed 58 | } 59 | 60 | fn join_mut(&mut self, other: Self) -> bool { 61 | let mut changed = false; 62 | $(changed |= self.0.$i.join_mut(other.0.$i);)* 63 | changed 64 | } 65 | 66 | fn meet(self, other: Self) -> Self { 67 | Product(($(self.0.$i.meet(other.0.$i)),*,)) 68 | } 69 | 70 | fn join(self, other: Self) -> Self { 71 | Product(($(self.0.$i.join(other.0.$i)),*,)) 72 | } 73 | } 74 | 75 | impl< $([]: BoundedLattice),* > BoundedLattice for Product<($([]),*,)> where Product<($([]),*,)>: Lattice { 76 | fn bottom() -> Self { 77 | Product(($([]::bottom()),*,)) 78 | } 79 | 80 | fn top() -> Self { 81 | Product(($([]::top()),*,)) 82 | } 83 | } 84 | );}; 85 | } 86 | tuple_lattice_impl!(0); 87 | tuple_lattice_impl!(0, 1); 88 | tuple_lattice_impl!(0, 1, 2); 89 | tuple_lattice_impl!(0, 1, 2, 3); 90 | tuple_lattice_impl!(0, 1, 2, 3, 4); 91 | tuple_lattice_impl!(0, 1, 2, 3, 4, 5); 92 | tuple_lattice_impl!(0, 1, 2, 3, 4, 5, 6); 93 | tuple_lattice_impl!(0, 1, 2, 3, 4, 5, 6, 7); 94 | tuple_lattice_impl!(0, 1, 2, 3, 4, 5, 6, 7, 8); 95 | tuple_lattice_impl!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); 96 | tuple_lattice_impl!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); 97 | 98 | #[test] 99 | fn test_product_lattice() { 100 | let t1 = Product((1, 3)); 101 | let t2 = Product((0, 10)); 102 | 103 | assert_eq!(Lattice::meet(t1, t2), Product((0, 3))); 104 | assert_eq!(Lattice::join(t1, t2), Product((1, 10))); 105 | assert_eq!(Product::<(u32, u32)>::bottom(), Product((0, 0))); 106 | 107 | assert!(Product((1, 3)) < Product((2, 3))); 108 | assert!(!{ Product((1, 4)) < Product((2, 3)) }); 109 | assert!(Product((1, 4)).partial_cmp(&Product((2, 3))).is_none()); 110 | } 111 | 112 | impl PartialOrd for Product<[T; N]> { 113 | fn partial_cmp(&self, other: &Self) -> Option { 114 | let mut ord = Ordering::Equal; 115 | for i in 0..N { 116 | let ith_ord = self.0[i].partial_cmp(&other.0[i]); 117 | match ith_ord { 118 | None => return None, 119 | Some(ith_ord) => match combine_orderings(ith_ord, ord) { 120 | Some(new_ord) => ord = new_ord, 121 | None => return None, 122 | }, 123 | } 124 | } 125 | Some(ord) 126 | } 127 | } 128 | 129 | #[test] 130 | fn test_product_of_array_partial_ord() { 131 | let a1 = Product([1, 2, 3]); 132 | let a2 = Product([1, 3, 4]); 133 | assert_eq!(a1.partial_cmp(&a2), Some(Ordering::Less)); 134 | assert_eq!(a2.partial_cmp(&a1), Some(Ordering::Greater)); 135 | 136 | let a3 = Product([0, 2, 4]); 137 | assert_eq!(a1.partial_cmp(&a3), None); 138 | assert_eq!(a3.partial_cmp(&a1), None); 139 | 140 | assert_eq!(a2.partial_cmp(&a3), Some(Ordering::Greater)); 141 | assert_eq!(a3.partial_cmp(&a2), Some(Ordering::Less)); 142 | } 143 | 144 | impl Lattice for Product<[T; N]> { 145 | fn meet_mut(&mut self, other: Self) -> bool { 146 | let mut changed = false; 147 | for (l, r) in self.0.iter_mut().zip(other.0) { 148 | changed |= l.meet_mut(r); 149 | } 150 | changed 151 | } 152 | 153 | fn join_mut(&mut self, other: Self) -> bool { 154 | let mut changed = false; 155 | for (l, r) in self.0.iter_mut().zip(other.0) { 156 | changed |= l.join_mut(r); 157 | } 158 | changed 159 | } 160 | } 161 | 162 | impl BoundedLattice for Product<[T; N]> { 163 | fn bottom() -> Self { 164 | // unstable: 165 | // Product(std::array::from_fn(|_| T::bottom())) 166 | Product([(); N].map(|_| T::bottom())) 167 | } 168 | 169 | fn top() -> Self { Product([(); N].map(|_| T::top())) } 170 | } 171 | 172 | #[test] 173 | fn test_product_of_array_lattice() { 174 | let a1 = Product([1, 5, 3]); 175 | let a2 = Product([1, 3, 4]); 176 | let a1_a2_meet = Product([1, 3, 3]); 177 | let a1_a2_join = Product([1, 5, 4]); 178 | assert_eq!(a1.meet(a2), a1_a2_meet); 179 | assert_eq!(a1.join(a2), a1_a2_join); 180 | 181 | assert_eq!(Product([0; 3]), Product::<[u32; 3]>::bottom()); 182 | assert_eq!(Product([true; 4]), Product::<[bool; 4]>::top()); 183 | } 184 | -------------------------------------------------------------------------------- /ascent_base/src/lattice/set.rs: -------------------------------------------------------------------------------- 1 | use std::cmp::Ordering; 2 | use std::collections::BTreeSet; 3 | use std::fmt::{Debug, Formatter}; 4 | use std::hash::Hash; 5 | use std::ops::Deref; 6 | 7 | use super::Lattice; 8 | 9 | /// A set type that implements the `Lattice` trait 10 | #[derive(Clone, PartialEq, Eq, Hash)] 11 | pub struct Set(pub BTreeSet); 12 | 13 | impl Set { 14 | /// Creates a `Set` containing only `item` 15 | pub fn singleton(item: T) -> Self { 16 | let mut set = BTreeSet::new(); 17 | set.insert(item); 18 | Set(set) 19 | } 20 | } 21 | 22 | impl Default for Set { 23 | fn default() -> Self { Self(Default::default()) } 24 | } 25 | 26 | impl Debug for Set { 27 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { self.0.fmt(f) } 28 | } 29 | 30 | impl Deref for Set { 31 | type Target = BTreeSet; 32 | 33 | fn deref(&self) -> &Self::Target { &self.0 } 34 | } 35 | 36 | impl PartialOrd for Set { 37 | fn partial_cmp(&self, other: &Self) -> Option { 38 | if self.0 == other.0 { 39 | Some(Ordering::Equal) 40 | } else if self.0.is_subset(&other.0) { 41 | Some(Ordering::Less) 42 | } else if self.0.is_superset(&other.0) { 43 | Some(Ordering::Greater) 44 | } else { 45 | None 46 | } 47 | } 48 | } 49 | 50 | impl Lattice for Set { 51 | fn meet_mut(&mut self, mut other: Self) -> bool { 52 | let self_len = self.0.len(); 53 | let mut old_self = BTreeSet::new(); 54 | std::mem::swap(&mut self.0, &mut old_self); 55 | if self.0.len() > other.0.len() { 56 | std::mem::swap(self, &mut other); 57 | } 58 | for item in old_self.into_iter() { 59 | if other.0.contains(&item) { 60 | self.0.insert(item); 61 | } 62 | } 63 | self_len != self.0.len() 64 | } 65 | 66 | fn join_mut(&mut self, mut other: Self) -> bool { 67 | let self_len = self.0.len(); 68 | if self_len < other.0.len() { 69 | std::mem::swap(self, &mut other); 70 | } 71 | for item in other.0.into_iter() { 72 | self.0.insert(item); 73 | } 74 | 75 | self_len != self.0.len() 76 | } 77 | 78 | fn meet(mut self, other: Self) -> Self { 79 | self.meet_mut(other); 80 | self 81 | } 82 | 83 | fn join(mut self, other: Self) -> Self { 84 | self.join_mut(other); 85 | self 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /ascent_base/src/lattice/tuple.rs: -------------------------------------------------------------------------------- 1 | use paste::paste; 2 | 3 | use super::{BoundedLattice, Lattice}; 4 | 5 | macro_rules! tuple_lattice_impl { 6 | ($($i:tt),*) => { 7 | paste!( 8 | impl< $([]),* > Lattice for ($([]),*,) where ($([]),*,): Ord { 9 | fn meet_mut(&mut self, other: Self) -> bool { 10 | use std::cmp::Ordering::*; 11 | match (&*self).cmp(&other) { 12 | Less | Equal => false, 13 | Greater => { 14 | *self = other; 15 | true 16 | } 17 | } 18 | } 19 | 20 | fn join_mut(&mut self, other: Self) -> bool { 21 | use std::cmp::Ordering::*; 22 | match (&*self).cmp(&other) { 23 | Greater | Equal => false, 24 | Less => { 25 | *self = other; 26 | true 27 | } 28 | } 29 | } 30 | 31 | fn meet(self, other: Self) -> Self { 32 | self.min(other) 33 | } 34 | 35 | fn join(self, other: Self) -> Self { 36 | self.max(other) 37 | } 38 | } 39 | 40 | impl< $([]),* > BoundedLattice for ($([]),*,) where $([]: BoundedLattice + Ord),* { 41 | fn bottom() -> Self { 42 | ($([]::bottom(),)*) 43 | } 44 | 45 | fn top() -> Self { 46 | ($([]::top(),)*) 47 | } 48 | } 49 | ); 50 | }; 51 | } 52 | 53 | tuple_lattice_impl!(0); 54 | tuple_lattice_impl!(0, 1); 55 | tuple_lattice_impl!(0, 1, 2); 56 | tuple_lattice_impl!(0, 1, 2, 3); 57 | tuple_lattice_impl!(0, 1, 2, 3, 4); 58 | tuple_lattice_impl!(0, 1, 2, 3, 4, 5); 59 | tuple_lattice_impl!(0, 1, 2, 3, 4, 5, 6); 60 | tuple_lattice_impl!(0, 1, 2, 3, 4, 5, 6, 7); 61 | tuple_lattice_impl!(0, 1, 2, 3, 4, 5, 6, 7, 8); 62 | tuple_lattice_impl!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); 63 | tuple_lattice_impl!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); 64 | 65 | impl super::Lattice for () { 66 | fn meet_mut(&mut self, _other: Self) -> bool { false } 67 | fn join_mut(&mut self, _other: Self) -> bool { false } 68 | fn meet(self, _other: Self) -> Self {} 69 | fn join(self, _other: Self) -> Self {} 70 | } 71 | 72 | impl BoundedLattice for () { 73 | fn bottom() -> Self {} 74 | fn top() -> Self {} 75 | } 76 | -------------------------------------------------------------------------------- /ascent_base/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod lattice; 2 | #[doc(hidden)] 3 | pub mod util; 4 | pub use lattice::{Dual, Lattice}; 5 | -------------------------------------------------------------------------------- /ascent_base/src/util.rs: -------------------------------------------------------------------------------- 1 | //! internal utility functions defined here 2 | //! 3 | //! CAUTION: anything defined here is subject to change in semver-compatible releases 4 | 5 | /// update `reference` in-place using the provided closure 6 | pub fn update(reference: &mut T, f: impl FnOnce(T) -> T) { 7 | let ref_taken = std::mem::take(reference); 8 | let new_val = f(ref_taken); 9 | *reference = new_val; 10 | } 11 | 12 | #[test] 13 | fn test_update() { 14 | let mut vec = vec![1, 2, 3]; 15 | update(&mut vec, |mut v| { 16 | v.push(4); 17 | v 18 | }); 19 | assert_eq!(vec, vec![1, 2, 3, 4]); 20 | } 21 | -------------------------------------------------------------------------------- /ascent_macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ascent_macro" 3 | version.workspace = true 4 | edition = "2021" 5 | authors = ["Arash Sahebolamri"] 6 | repository = "https://github.com/s-arash/ascent" 7 | license = "MIT" 8 | description = "implementation of ascent macros" 9 | 10 | [lib] 11 | proc-macro = true 12 | 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [dependencies] 16 | syn = { version = "2.0.57", features = ["derive", "full", "extra-traits", "visit-mut"] } 17 | quote = "1.0" 18 | ascent_base = { workspace = true } 19 | proc-macro2 = "1.0" 20 | itertools = "0.13" 21 | petgraph = "0.6.0" 22 | derive-syn-parse = "0.2.0" 23 | lazy_static = "1.4.0" 24 | duplicate = { version = "2.0", default-features = false } 25 | 26 | [dev-dependencies] 27 | ascent = { path = "../ascent" } 28 | rayon = "1.5" 29 | crossbeam = "0.8" 30 | -------------------------------------------------------------------------------- /ascent_macro/rust-toolchain: -------------------------------------------------------------------------------- 1 | stable -------------------------------------------------------------------------------- /ascent_macro/src/scratchpad.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /ascent_macro/src/scratchpad_template.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused_imports)] 2 | use std::{clone, cmp::max, rc::Rc}; 3 | use std::ops::Deref; 4 | use std::hash::Hash; 5 | use std::fmt::Debug; 6 | 7 | 8 | #[allow(dead_code)] 9 | pub trait Atom: 10 | From + Into + Copy + Clone + std::fmt::Debug + Eq + Ord + Hash + Sync + Send + 'static 11 | { 12 | fn index(self) -> usize; 13 | } 14 | 15 | #[allow(dead_code)] 16 | pub trait FactTypes: Copy + Clone + Debug { 17 | type Origin: Atom; 18 | type Loan: Atom; 19 | type Point: Atom; 20 | type Variable: Atom; 21 | type Path: Atom; 22 | } 23 | 24 | #[warn(warnings)] 25 | #[allow(unused_imports)] 26 | #[allow(dead_code)] 27 | #[allow(redundant_semicolons)] 28 | #[cfg(test)] 29 | fn _test() { 30 | use ascent::aggregators::*; 31 | use ascent::lattice::set::Set; 32 | use ascent::Dual; 33 | 34 | use ascent::rel as custom_ds; 35 | todo!("here"); 36 | ; 37 | } -------------------------------------------------------------------------------- /ascent_macro/src/test_errors.rs: -------------------------------------------------------------------------------- 1 | #![cfg(test)] 2 | use crate::{AscentMacroKind, ascent_impl}; 3 | 4 | #[test] 5 | fn test_agg_not_stratifiable() { 6 | let inp = quote! { 7 | relation foo(i32, i32, i32); 8 | relation bar(i32, i32); 9 | relation baz(i32); 10 | 11 | baz(x) <-- 12 | foo(x, _, _), 13 | !bar(_, x); 14 | 15 | bar(x, x + 1) <-- baz(x); 16 | }; 17 | let res = ascent_impl(inp, AscentMacroKind::default()); 18 | println!("res: {:?}", res); 19 | assert!(res.is_err()); 20 | assert!(res.unwrap_err().to_string().contains("bar")); 21 | } 22 | -------------------------------------------------------------------------------- /ascent_tests/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ascent_tests" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | 10 | ascent = { path = "../ascent", default-features = false } 11 | bencher = "0.1.5" 12 | derive_more = "0.99.16" 13 | itertools = "0.14" 14 | quote = "1.0" 15 | arrayvec = "0.7" 16 | const-fnv1a-hash = "1.0.1" 17 | 18 | [dev-dependencies] 19 | rand = "0.9" 20 | 21 | [[bench]] 22 | name = "benches" 23 | harness = false 24 | 25 | [features] 26 | par = ["ascent/par"] -------------------------------------------------------------------------------- /ascent_tests/benches/benches.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | use std::collections::{BTreeMap, HashMap}; 4 | use std::time::Instant; 5 | 6 | use ascent::ascent; 7 | use ascent::lattice::Dual; 8 | use ascent_tests::ascent_m_par; 9 | 10 | mod tc { 11 | use ascent::ascent; 12 | 13 | ascent! { 14 | relation edge(i32, i32); 15 | relation path(i32, i32); 16 | // edge(x, x + 1) <-- for x in (0..1000); 17 | path(*x, *y) <-- edge(x,y); 18 | path(*x, *z) <-- edge(x,y), path(y, z); 19 | // path(*x, *z) <-- path(x,y), edge(y, z); 20 | 21 | } 22 | } 23 | 24 | fn loop_graph(nodes: usize) -> Vec<(i32, i32)> { 25 | let mut res = vec![]; 26 | let nodes = nodes as i32; 27 | for x in 0..nodes { 28 | res.push((x, (x + 1) % nodes)); 29 | } 30 | res 31 | } 32 | 33 | fn complete_graph(nodes: usize) -> Vec<(i32, i32, u32)> { 34 | let mut res = vec![]; 35 | let nodes = nodes as i32; 36 | for x in 0..nodes { 37 | for y in 0..nodes { 38 | if x != y { 39 | res.push((x, y, 1)); 40 | } 41 | } 42 | } 43 | res 44 | } 45 | 46 | fn bench_tc(nodes_count: i32) { 47 | let mut tc = tc::AscentProgram::default(); 48 | 49 | for i in 0..nodes_count { 50 | tc.edge.push((i, i + 1)); 51 | } 52 | 53 | let before = Instant::now(); 54 | tc.run(); 55 | let elapsed = before.elapsed(); 56 | 57 | println!("tc for {} nodes took {:?}", nodes_count, elapsed); 58 | println!("path size: {}", tc.path.len()); 59 | } 60 | 61 | fn test_dl_lattice1() { 62 | ascent! { 63 | lattice shortest_path(i32, i32, Dual); 64 | relation edge(i32, i32, u32); 65 | 66 | shortest_path(*x, *y, Dual(*w)) <-- edge(x, y, w); 67 | shortest_path(*x, *z, Dual(w + l.0)) <-- edge(x, y, w), shortest_path(y, z, l); 68 | 69 | edge(1, 2, x + 30) <-- for x in 0..10000; 70 | edge(2, 3, x + 50) <-- for x in 0..10000; 71 | edge(1, 3, x + 40) <-- for x in 0..10000; 72 | edge(2, 4, x + 100) <-- for x in 0..10000; 73 | edge(1, 4, x + 200) <-- for x in 0..10000; 74 | } 75 | let mut prog = AscentProgram::default(); 76 | prog.run(); 77 | // println!("shortest_path ({} tuples):", prog.shortest_path.len()); 78 | //println!("{:?}", prog.shortest_path); 79 | for _i in prog.shortest_path.iter() {} 80 | // println!("{}", AscentProgram::summary()); 81 | // assert!(rels_equal(prog.shortest_path, [(1,2, Dual(30)), (1, 3, Dual(40)), (1,4, Dual(130)), (2,3, Dual(50)), (2, 4, Dual(100))])) 82 | } 83 | 84 | fn bench_lattice() { 85 | let iterations = 100; 86 | let before = Instant::now(); 87 | for _ in 0..iterations { 88 | test_dl_lattice1(); 89 | } 90 | let elapsed = before.elapsed(); 91 | println!("average time: {:?}", elapsed / iterations); 92 | } 93 | 94 | fn bench_tc_path_join_path(nodes_count: i32) { 95 | ascent_m_par! { 96 | // #![include_rule_times] 97 | struct TCPathJoinPath; 98 | relation edge(i32, i32); 99 | relation path(i32, i32); 100 | path(x, z) <-- path(x,y), path(y, z); 101 | path(x, y) <-- edge(x,y); 102 | } 103 | let mut tc = TCPathJoinPath::default(); 104 | println!("{}", TCPathJoinPath::summary()); 105 | 106 | for i in 0..nodes_count { 107 | tc.edge.push((i, i + 1)); 108 | } 109 | 110 | let before = Instant::now(); 111 | tc.run(); 112 | let elapsed = before.elapsed(); 113 | println!("tc path_join_path for {} nodes took {:?}", nodes_count, elapsed); 114 | // println!("summary: \n{}", tc.scc_times_summary()); 115 | println!("path size: {}", tc.path.len()); 116 | } 117 | 118 | fn bench_hash() { 119 | let mut hm = HashMap::new(); 120 | let mut bt = BTreeMap::new(); 121 | 122 | let iters = 10_000_000; 123 | 124 | let random_nums = rand::seq::index::sample(&mut rand::rng(), iters, iters); 125 | 126 | let before = Instant::now(); 127 | for i in random_nums.iter() { 128 | hm.insert((i, i, i), i * 2); 129 | } 130 | println!("hm took {:?}", before.elapsed()); 131 | 132 | let before = Instant::now(); 133 | for i in random_nums.iter() { 134 | bt.insert((i, i, i), i * 2); 135 | } 136 | println!("btree took {:?}", before.elapsed()); 137 | } 138 | fn bench_tc_for_graph(graph: Vec<(i32, i32)>, name: &str) { 139 | let before = Instant::now(); 140 | let mut tc = tc::AscentProgram::default(); 141 | tc.edge = graph; 142 | tc.run(); 143 | let elapsed = before.elapsed(); 144 | println!("tc for {} took {:?}", name, elapsed); 145 | // println!("summary: \n{}", tc.scc_times_summary()); 146 | println!("path size: {}", tc.path.len()); 147 | } 148 | 149 | fn main() { 150 | // bench_tc(1000); 151 | bench_tc_path_join_path(1000); 152 | // bench_tc_for_graph(loop_graph(4000), "loop 4000"); 153 | //bench_lattice(); 154 | // bench_hash(); 155 | } 156 | -------------------------------------------------------------------------------- /ascent_tests/rust-toolchain: -------------------------------------------------------------------------------- 1 | stable -------------------------------------------------------------------------------- /ascent_tests/src/agg_tests.rs: -------------------------------------------------------------------------------- 1 | #![cfg(test)] 2 | use ascent::{ascent, ascent_run}; 3 | use itertools::Itertools; 4 | 5 | use crate::utils::rels_equal; 6 | use crate::{ascent_run_m_par, assert_rels_eq}; 7 | 8 | fn percentile<'a, TInputIter>(p: f32) -> impl Fn(TInputIter) -> std::option::IntoIter 9 | where TInputIter: Iterator { 10 | move |inp| { 11 | let sorted = inp.map(|tuple| *tuple.0).sorted().collect_vec(); 12 | let p_index = (sorted.len() as f32 * p / 100.0) as usize; 13 | let p_index = p_index.clamp(0, sorted.len() - 1); 14 | sorted.get(p_index).cloned().into_iter() 15 | } 16 | } 17 | 18 | #[test] 19 | fn test_ascent_agg3() { 20 | let res = ascent_run_m_par! { 21 | relation foo(i32, i32); 22 | relation bar(i32, i32, i32); 23 | relation baz(i32, i32); 24 | foo(1, 2); 25 | foo(10, 11); 26 | 27 | bar(1, x, y), 28 | bar(10, x * 10, y * 10), 29 | bar(100, x * 100, y * 100) <-- for (x, y) in (1..100).map(|x| (x, x * 2)); 30 | 31 | baz(a, x_75th_p) <-- 32 | foo(a, _), 33 | agg x_75th_p = (percentile(75.0))(x) in bar(a, x, _); 34 | }; 35 | // println!("{}", res.summary()); 36 | println!("baz: {:?}", res.baz); 37 | assert!(rels_equal([(1, 75), (10, 750)], res.baz)); 38 | } 39 | 40 | #[test] 41 | fn test_ascent_agg4() { 42 | use ascent::aggregators::*; 43 | let res = ascent_run_m_par! { 44 | relation foo(i32, i32); 45 | relation bar(i32, i32, i32); 46 | relation baz(i32, i32, i32); 47 | foo(1, 2); 48 | foo(10, 11); 49 | 50 | bar(1, x, y), 51 | bar(10, x * 10, y * 10), 52 | bar(100, x * 100, y * 100) <-- for (x, y) in (1..100).map(|x| (x, x * 2)); 53 | 54 | baz(a, x_mean as i32, y_mean as i32) <-- 55 | foo(a, _), 56 | agg x_mean = mean(x) in bar(a, x, _), 57 | agg y_mean = mean(y) in bar(a, _, y); 58 | 59 | }; 60 | // println!("{}", res.summary()); 61 | println!("baz: {:?}", res.baz); 62 | assert!(rels_equal([(1, 50, 100), (10, 500, 1000)], res.baz)); 63 | } 64 | 65 | #[test] 66 | fn test_ascent_negation() { 67 | use ascent::aggregators::*; 68 | let res = ascent_run_m_par! { 69 | relation foo(i32, i32); 70 | relation bar(i32, i32, i32); 71 | relation baz(i32, i32); 72 | relation baz2(i32, i32); 73 | 74 | foo(0, 1); 75 | foo(1, 2); 76 | foo(10, 11); 77 | foo(100, 101); 78 | 79 | bar(1, 2, 102); 80 | bar(10, 11, 20); 81 | bar(10, 11, 12); 82 | 83 | baz(x, y) <-- 84 | foo(x, y), 85 | !bar(x, y, _); 86 | 87 | // equivalent to: 88 | baz2(x, y) <-- 89 | foo(x, y), 90 | agg () = not() in bar(x, y, _); 91 | }; 92 | // println!("{}", res.summary()); 93 | println!("baz: {:?}", res.baz); 94 | assert!(rels_equal([(0, 1), (100, 101)], res.baz)); 95 | assert!(rels_equal([(0, 1), (100, 101)], res.baz2)); 96 | } 97 | 98 | #[test] 99 | fn test_ascent_negation2() { 100 | use ascent::aggregators::*; 101 | let res = ascent_run_m_par! { 102 | relation foo(i32, i32); 103 | relation bar(i32, i32); 104 | relation baz(i32, i32); 105 | relation baz2(i32, i32); 106 | 107 | foo(0, 1); 108 | foo(1, 2); 109 | foo(10, 11); 110 | foo(100, 101); 111 | 112 | bar(1, 2); 113 | bar(10, 11); 114 | bar(10, 11); 115 | 116 | baz(x, y) <-- 117 | foo(x, y), 118 | !bar(x, y); 119 | 120 | // equivalent to: 121 | baz2(x, y) <-- 122 | foo(x, y), 123 | agg () = not() in bar(x, y); 124 | }; 125 | // println!("{}", res.summary()); 126 | println!("baz: {:?}", res.baz); 127 | assert_rels_eq!([(0, 1), (100, 101)], res.baz); 128 | assert_rels_eq!([(0, 1), (100, 101)], res.baz2); 129 | } 130 | 131 | #[test] 132 | fn test_ascent_negation3() { 133 | use ascent::aggregators::*; 134 | let res = ascent_run_m_par! { 135 | relation foo(i32, i32); 136 | relation bar(i32, i32, i32); 137 | relation baz(i32, i32); 138 | 139 | foo(0, 1); 140 | foo(1, 2); 141 | foo(10, 11); 142 | foo(100, 101); 143 | 144 | bar(1, 2, 3); 145 | bar(10, 11, 13); 146 | 147 | baz(x, y) <-- 148 | foo(x, y), 149 | !bar(x, y, y + 1); 150 | }; 151 | // println!("{}", res.summary()); 152 | println!("baz: {:?}", res.baz); 153 | assert!(rels_equal([(0, 1), (10, 11), (100, 101)], res.baz)); 154 | } 155 | 156 | #[test] 157 | fn test_ascent_agg_simple() { 158 | use ascent::aggregators::*; 159 | let res = ascent_run_m_par! { 160 | relation foo(i32); 161 | foo(0); foo(10); 162 | 163 | relation bar(i32); 164 | bar(m as i32) <-- agg m = mean(x) in foo(x); 165 | }; 166 | assert!(rels_equal([(5,)], res.bar)); 167 | } 168 | 169 | // Must fail to compile: 170 | // #[test] 171 | // fn test_ascent_agg_not_stratifiable(){ 172 | // use ascent::aggregators::*; 173 | // let res = ascent_run!{ 174 | // relation foo(i32, i32, i32); 175 | // relation bar(i32, i32); 176 | // relation baz(i32); 177 | 178 | // baz(x) <-- 179 | // foo(x, _, _), 180 | // !bar(_, x); 181 | 182 | // bar(x, x + 1) <-- baz(x); 183 | // }; 184 | // assert!(rels_equal([(5,)], res.bar)); 185 | // } 186 | -------------------------------------------------------------------------------- /ascent_tests/src/ascent_maybe_par.rs: -------------------------------------------------------------------------------- 1 | use std::sync::RwLock; 2 | 3 | #[cfg(not(feature = "par"))] 4 | #[macro_export] 5 | macro_rules! ascent_m_par { 6 | ($($tt: tt)*) => { 7 | ascent::ascent!{ $($tt)* } 8 | }; 9 | } 10 | 11 | #[cfg(feature = "par")] 12 | #[macro_export] 13 | macro_rules! ascent_m_par { 14 | ($($tt: tt)*) => { 15 | ascent::ascent_par!{ $($tt)* } 16 | }; 17 | } 18 | 19 | #[cfg(not(feature = "par"))] 20 | #[macro_export] 21 | macro_rules! ascent_run_m_par { 22 | ($($tt: tt)*) => { 23 | ascent::ascent_run!{ $($tt)* } 24 | }; 25 | } 26 | 27 | #[cfg(feature = "par")] 28 | #[macro_export] 29 | macro_rules! ascent_run_m_par { 30 | ($($tt: tt)*) => { 31 | ascent::ascent_run_par!{ $($tt)* } 32 | }; 33 | } 34 | 35 | #[cfg(not(feature = "par"))] 36 | #[allow(dead_code)] 37 | pub fn lat_to_vec(vec: Vec) -> Vec { vec } 38 | 39 | #[cfg(feature = "par")] 40 | #[allow(dead_code)] 41 | pub fn lat_to_vec(vec: ascent::boxcar::Vec>) -> Vec { 42 | vec.into_iter().map(|x| x.into_inner().unwrap()).collect() 43 | } 44 | -------------------------------------------------------------------------------- /ascent_tests/src/bin/tc.rs: -------------------------------------------------------------------------------- 1 | use std::time::Instant; 2 | 3 | use ascent_tests::ascent_m_par; 4 | 5 | ascent_m_par! { 6 | struct TC; 7 | relation edge(i32, i32); 8 | relation path(i32, i32); 9 | 10 | path(*x, *y) <-- edge(x,y); 11 | path(*x, *z) <-- path(x, y), edge(y, z); 12 | } 13 | 14 | fn main() { 15 | let edges = (0..5000).map(|x| (x, x + 1)).collect(); 16 | let mut prog = TC::default(); 17 | 18 | prog.edge = edges; 19 | 20 | let before = Instant::now(); 21 | prog.run(); 22 | let took = before.elapsed(); 23 | println!("path len: {}", prog.path.len()); 24 | println!("took {took:?}"); 25 | } 26 | -------------------------------------------------------------------------------- /ascent_tests/src/example_tests.rs: -------------------------------------------------------------------------------- 1 | use std::hash::Hash; 2 | use std::rc::Rc; 3 | 4 | use ascent::aggregators::mean; 5 | use ascent::{ascent, ascent_run}; 6 | 7 | use crate::utils::rels_equal; 8 | use crate::{ascent_m_par, ascent_run_m_par, assert_rels_eq}; 9 | 10 | #[test] 11 | fn test_generators_conditions_example() { 12 | let res = ascent_run! { 13 | relation node(i32, Rc>); 14 | relation edge(i32, i32); 15 | 16 | node(1, Rc::new(vec![2, 3])); 17 | node(2, Rc::new(vec![3, 4])); 18 | 19 | edge(x, y) <-- 20 | node(x, neighbors), 21 | for &y in neighbors.iter(); 22 | }; 23 | println!("edges: {:?}", res.edge); 24 | assert!(rels_equal(&res.edge, &[(1, 2), (1, 3), (2, 3), (2, 4)])); 25 | } 26 | 27 | #[test] 28 | fn test_agg_example() { 29 | type Student = u32; 30 | type Course = u32; 31 | type Grade = u16; 32 | ascent_m_par! { 33 | relation student(Student); 34 | relation course_grade(Student, Course, Grade); 35 | relation avg_grade(Student, Grade); 36 | 37 | avg_grade(s, avg as Grade) <-- 38 | student(s), 39 | agg avg = mean(g) in course_grade(s, _, g); 40 | } 41 | let mut prog = AscentProgram::default(); 42 | prog.student = FromIterator::from_iter([(1,), (2,)]); 43 | prog.course_grade = FromIterator::from_iter([(1, 600, 60), (1, 602, 80), (2, 602, 70), (2, 605, 90)]); 44 | prog.run(); 45 | println!("avg grade: {:?}", prog.avg_grade); 46 | assert_rels_eq!(&prog.avg_grade, &[(1, 70), (2, 80)]); 47 | } 48 | 49 | #[test] 50 | fn test_tc_example() { 51 | fn tc(r: Vec<(i32, i32)>, reflexive: bool) -> Vec<(i32, i32)> { 52 | ascent_run! { 53 | relation r(i32, i32) = r; 54 | relation tc(i32, i32); 55 | tc(x, y) <-- r(x, y); 56 | tc(x, z) <-- r(x, y), tc(y, z); 57 | tc(x, x), tc(y, y) <-- if reflexive, r(x, y); 58 | } 59 | .tc 60 | } 61 | let r = vec![(1, 2), (2, 4), (3, 1)]; 62 | println!("tc: {:?}", tc(r.clone(), true)); 63 | println!("reflexive tc: {:?}", tc(r.clone(), true)); 64 | assert_rels_eq!(tc(r.clone(), true), vec![ 65 | (1, 1), 66 | (2, 2), 67 | (3, 3), 68 | (4, 4), 69 | (1, 2), 70 | (1, 4), 71 | (2, 4), 72 | (3, 1), 73 | (3, 2), 74 | (3, 4) 75 | ]); 76 | } 77 | 78 | #[test] 79 | fn test_generic_tc_example() { 80 | fn tc(r: Vec<(N, N)>, reflexive: bool) -> Vec<(N, N)> 81 | where N: Clone + Hash + Eq { 82 | ascent_run! { 83 | struct TC; 84 | relation r(N, N) = r; 85 | relation tc(N, N); 86 | tc(x, y) <-- r(x, y); 87 | tc(x, z) <-- r(x, y), tc(y, z); 88 | tc(x, x), tc(y, y) <-- if reflexive, r(x, y); 89 | } 90 | .tc 91 | } 92 | let r = vec![(1, 2), (2, 4), (3, 1)]; 93 | println!("tc: {:?}", tc(r.clone(), true)); 94 | println!("reflexive tc: {:?}", tc(r.clone(), true)); 95 | assert_rels_eq!(tc(r.clone(), true), vec![ 96 | (1, 1), 97 | (2, 2), 98 | (3, 3), 99 | (4, 4), 100 | (1, 2), 101 | (1, 4), 102 | (2, 4), 103 | (3, 1), 104 | (3, 2), 105 | (3, 4) 106 | ]); 107 | } 108 | 109 | #[test] 110 | fn test_generic_ty() { 111 | ascent! { 112 | struct AscentProgram where T: Clone + Hash + Eq; 113 | relation dummy(T); 114 | } 115 | 116 | struct Container(AscentProgram) 117 | where T: Clone + Hash + Eq; 118 | 119 | impl Container 120 | where T: Clone + Hash + Eq 121 | { 122 | fn run(&mut self) { self.0.run(); } 123 | } 124 | 125 | let mut container: Container = Container(AscentProgram::default()); 126 | container.run(); 127 | } 128 | 129 | #[test] 130 | fn test_generic_ty_with_divergent_impl_generics() { 131 | ascent! { 132 | struct AscentProgram; 133 | impl AscentProgram where T: Clone + Hash + Eq; 134 | relation dummy(T); 135 | } 136 | 137 | struct Container(AscentProgram); 138 | 139 | impl Container 140 | where T: Clone + Hash + Eq 141 | { 142 | fn run(&mut self) { self.0.run(); } 143 | } 144 | 145 | let mut container: Container = Container(AscentProgram::default()); 146 | container.run(); 147 | } 148 | 149 | #[test] 150 | fn test_borrowed_strings() { 151 | ascent_m_par! { 152 | struct Ancestry<'a>; 153 | relation parent(&'a str, &'a str); 154 | relation ancestor(&'a str,&'a str); 155 | 156 | ancestor(p, c) <-- parent(p, c); 157 | 158 | ancestor(p, gc) <-- 159 | parent(p, c), ancestor(c, gc); 160 | } 161 | 162 | let james = "James".to_string(); 163 | let harry = "Harry".to_string(); 164 | let albus = "Albus".to_string(); 165 | 166 | let parent_rel = vec![(james.clone(), harry.clone()), (harry.clone(), albus.clone())]; 167 | 168 | let mut prog = Ancestry::default(); 169 | // prog.parent = vec![(&zal[..], &rostam[..]), (&rostam[..], &sohrab[..])]; 170 | prog.parent = parent_rel.iter().map(|(p, c)| (&p[..], &c[..])).collect(); 171 | prog.run(); 172 | println!("ancestors: {:?}", prog.ancestor); 173 | assert_eq!(prog.ancestor.len(), 3); 174 | } 175 | 176 | #[test] 177 | fn test_borrowed_strings_2() { 178 | fn ancestry_fn<'a>(parent_rel: impl Iterator) -> Vec<(&'a str, &'a str)> { 179 | ascent_run_m_par! { 180 | struct Ancestry<'a>; 181 | relation parent(&'a str, &'a str) = parent_rel.collect(); 182 | relation ancestor(&'a str,&'a str); 183 | 184 | ancestor(p, c) <-- parent(p, c); 185 | 186 | ancestor(p, gc) <-- 187 | parent(p, c), ancestor(c, gc); 188 | } 189 | .ancestor 190 | .into_iter() 191 | .collect() 192 | } 193 | 194 | let james = "James".to_string(); 195 | let harry = "Harry".to_string(); 196 | let albus = "Albus".to_string(); 197 | 198 | let parent_rel = vec![(james.clone(), harry.clone()), (harry.clone(), albus.clone())]; 199 | let ancestor = ancestry_fn(parent_rel.iter().map(|(x, y)| (&x[..], &y[..]))); 200 | println!("ancestors: {:?}", ancestor); 201 | assert_eq!(ancestor.len(), 3); 202 | } 203 | -------------------------------------------------------------------------------- /ascent_tests/src/exps.rs: -------------------------------------------------------------------------------- 1 | #![allow(warnings)] 2 | use ascent::ascent; 3 | fn exp_type_eq() {} 4 | 5 | fn types_eq() -> bool { todo!() } 6 | 7 | macro_rules! run_combined { 8 | ($($b:tt)*) => { 9 | 10 | }; 11 | } 12 | 13 | // trait TypesEq { 14 | // const ARE_EQUAL : bool; 15 | // } 16 | 17 | // impl TypesEq for T { 18 | // const ARE_EQUAL : bool = true; 19 | // } 20 | 21 | // impl TypesEq for T { 22 | // const ARE_EQUAL : bool = false; 23 | // } 24 | 25 | fn exp_type_param(inp: T) { 26 | struct Inner { 27 | one: T, 28 | } 29 | let instance = Inner { one: inp }; 30 | } 31 | 32 | fn exp_rel_traits() { 33 | trait HasRel { 34 | type Fields; 35 | } 36 | 37 | ascent! { 38 | struct AscentProg1; 39 | relation foo(i32, i32); 40 | } 41 | 42 | ascent! { 43 | struct AscentProg2; 44 | relation foo(i32, i32); 45 | } 46 | 47 | impl HasRel<{ mangle("foo") }> for AscentProg1 { 48 | type Fields = (i32, i32); 49 | } 50 | 51 | impl HasRel<{ mangle("foo") }> for AscentProg2 { 52 | type Fields = (i32, i32); 53 | } 54 | 55 | run_combined!(AscentProg1::default(), AscentProg2::default() on foo); 56 | } 57 | const fn mangle(input: &str) -> u64 { const_fnv1a_hash::fnv1a_hash_str_64(input) } 58 | -------------------------------------------------------------------------------- /ascent_tests/src/include_source_tests.rs: -------------------------------------------------------------------------------- 1 | use ascent::{ascent_run, ascent_source}; 2 | 3 | mod base { 4 | ascent::ascent_source! { 5 | /// Defines `edge` and `path`, the transitive closure of `edge`. 6 | /// The type of a node is `Node` 7 | tc: 8 | 9 | /// `edge` degined in `tc` 10 | relation edge(Node, Node); 11 | relation path(Node, Node); 12 | path(x, y) <-- edge(x, y); 13 | path(x, z) <-- edge(x, y), path(y, z); 14 | } 15 | } 16 | 17 | ascent_source! { symmetric_edges: 18 | edge(x, y) <-- edge(y, x); 19 | } 20 | 21 | #[test] 22 | fn include_ascent_source() { 23 | type Node = usize; 24 | let res = ascent_run! { 25 | include_source!(base::tc); 26 | include_source!(symmetric_edges); 27 | /// `edge` defined in `ascent_run` 28 | relation edge(Node, Node); 29 | 30 | edge(1, 2), edge(2, 3), edge(3, 4); 31 | }; 32 | 33 | assert!(res.path.contains(&(4, 1))); 34 | } 35 | -------------------------------------------------------------------------------- /ascent_tests/src/lib.rs: -------------------------------------------------------------------------------- 1 | // #![feature(decl_macro)] 2 | #![allow(unused_imports)] 3 | #![allow(confusable_idents)] 4 | 5 | mod ascent_maybe_par; 6 | mod tests; 7 | pub mod utils; 8 | mod se; 9 | mod exps; 10 | mod analysis_exp; 11 | mod agg_tests; 12 | mod example_tests; 13 | mod macros_tests; 14 | mod include_source_tests; 15 | -------------------------------------------------------------------------------- /ascent_tests/src/macros_tests.rs: -------------------------------------------------------------------------------- 1 | #![cfg(test)] 2 | 3 | use ascent::ascent; 4 | 5 | use crate::assert_rels_eq; 6 | 7 | #[test] 8 | fn test_macro_in_macro() { 9 | ascent! { 10 | relation foo1(i32, i32); 11 | relation foo2(i32, i32); 12 | relation bar(i32 , i32); 13 | 14 | foo1(1, 2); 15 | foo1(2, 1); 16 | 17 | foo2(11, 12); 18 | foo2(12, 11); 19 | 20 | macro foo($x: ident, $y: ident){ 21 | (foo1($x, $y) | foo2($x, $y)), if $x < $y, 22 | } 23 | 24 | bar(x, y) <-- foo!(x, y); 25 | 26 | relation baz(i32); 27 | relation quax(i32); 28 | baz(1); baz(2); 29 | baz(11); baz(12); 30 | 31 | quax(y) <-- baz(x), foo!(x, y); 32 | }; 33 | let mut prog = AscentProgram::default(); 34 | prog.run(); 35 | assert_rels_eq!(prog.bar, [(1, 2), (11, 12)]); 36 | assert_rels_eq!(prog.quax, [(2,), (12,)]); 37 | } 38 | 39 | #[test] 40 | fn test_macro_in_macro2() { 41 | type Var = String; 42 | type Val = isize; 43 | #[derive(Clone, Eq, PartialEq, Hash)] 44 | enum Atomic { 45 | Val(Val), 46 | Var(Var), 47 | } 48 | ascent! { 49 | struct AscentProgram; 50 | relation σ(Var, Val); 51 | relation res(Atomic); 52 | 53 | macro ae($x: ident) { 54 | (res(?Atomic::Var(_var)), σ(_var, $x) | 55 | res(?Atomic::Val($x))) 56 | } 57 | 58 | relation res_val(Val); 59 | res_val(x) <-- ae!(x); 60 | 61 | relation res_val2(Val, Val); 62 | res_val2(x, y) <-- ae!(x), ae!(y); 63 | 64 | } 65 | 66 | let mut prog = AscentProgram::default(); 67 | 68 | prog.σ = vec![("x1".into(), 100), ("x2".into(), 200)]; 69 | prog.res = vec![(Atomic::Val(1000),), (Atomic::Var("x1".into()),)]; 70 | prog.run(); 71 | 72 | println!("res_val: {}\n{:?}", prog.res_val.len(), prog.res_val); 73 | println!("res_val2: {}\n{:?}", prog.res_val2.len(), prog.res_val2); 74 | 75 | assert_eq!(prog.res_val2.len(), prog.res_val.len().pow(2)); 76 | assert_rels_eq!(prog.res_val, [(100,), (1000,)]); 77 | } 78 | 79 | #[test] 80 | fn test_macro_in_macro3() { 81 | ascent! { 82 | relation edge(i32, i32); 83 | relation edge_rev(i32, i32); 84 | 85 | macro edge($x: expr, $y: expr) { 86 | edge($x, $y), edge_rev($y, $x) 87 | } 88 | 89 | edge!(1, 2); 90 | 91 | edge!(x, x + 1) <-- for x in 0..10; 92 | } 93 | 94 | let mut prog = AscentProgram::default(); 95 | prog.run(); 96 | 97 | assert_eq!(prog.edge.len(), prog.edge_rev.len()); 98 | } 99 | 100 | #[test] 101 | fn test_macro_in_macro4() { 102 | ascent! { 103 | relation foo(i32, i32); 104 | relation bar(i32, i32); 105 | 106 | macro foo_($x: expr, $y: expr) { foo($x, $y) } 107 | 108 | macro foo($x: expr, $y: expr) { 109 | let _x = $x, let _y = $y, foo_!(_x, _y) 110 | } 111 | 112 | foo(0, 1), foo(1, 2), foo(2, 3), foo(3, 4); 113 | bar(x, y) <-- foo(x, y), foo!(x + 1, y + 1), foo!(x + 2, y + 2), foo!(x + 3, y + 3); 114 | } 115 | 116 | let mut prog = AscentProgram::default(); 117 | prog.run(); 118 | 119 | assert_rels_eq!(prog.bar, [(0, 1)]); 120 | } 121 | 122 | #[test] 123 | fn test_macro_in_macro5() { 124 | type Lang = &'static str; 125 | type CompilerName = &'static str; 126 | ascent! { 127 | relation compiler(CompilerName, Lang, Lang); 128 | relation bad_compiler(CompilerName); 129 | 130 | relation can_compile_to(Lang, Lang); 131 | 132 | macro compiler($from: expr, $to: expr) { 133 | compiler(_name, $from, $to), !bad_compiler(_name) 134 | } 135 | 136 | can_compile_to(a, b) <-- compiler!(a, b); 137 | can_compile_to(a, c) <-- compiler!(a, b), can_compile_to(b, c); 138 | 139 | relation compiles_in_two_steps(Lang, Lang); 140 | compiles_in_two_steps(a, c) <-- compiler!(a, b), compiler!(b, c); 141 | } 142 | 143 | let mut prog = AscentProgram::default(); 144 | prog.compiler = vec![ 145 | ("Rustc", "Rust", "X86"), 146 | ("Rustc", "Rust", "WASM"), 147 | ("MyRandomCompiler", "Python", "Rust"), 148 | ("Cython", "Python", "C"), 149 | ("Clang", "C", "X86"), 150 | ]; 151 | prog.bad_compiler = vec![("MyRandomCompiler",)]; 152 | prog.run(); 153 | 154 | println!("can_compile_to: {:?}", prog.can_compile_to); 155 | println!("compiles_in_two_steps: {:?}", prog.compiles_in_two_steps); 156 | 157 | assert!(prog.can_compile_to.contains(&("Python", "X86"))); 158 | assert!(!prog.can_compile_to.contains(&("Python", "Rust"))); 159 | } 160 | 161 | #[test] 162 | fn test_macro_in_macro6() { 163 | ascent! { 164 | relation foo(i32, i32) = vec![(0, 1), (1, 2), (2, 3), (3, 4)]; 165 | 166 | macro foo_rev($y: expr, $x: expr) { 167 | foo!($x, $y), let x = $x, let y = $y 168 | } 169 | 170 | macro foo_($x: expr, $y: expr) { 171 | foo($x, $y) 172 | } 173 | 174 | macro foo($x: expr, $y: expr) { 175 | foo_!($x, $y), let x = $x, let y = $y, foo_!(x, y) 176 | } 177 | 178 | relation baz(i32, i32); 179 | relation baz_e(i32, i32); 180 | 181 | baz(x, z) <-- foo_rev!(y, x), foo_rev!(z, y); 182 | baz_e(x, z) <-- foo(x, y), foo(y, z); 183 | } 184 | 185 | let mut prog = AscentProgram::default(); 186 | prog.run(); 187 | 188 | println!("baz: {:?}", prog.baz); 189 | println!("baz_e: {:?}", prog.baz_e); 190 | 191 | assert_rels_eq!(prog.baz, prog.baz_e); 192 | } 193 | 194 | #[test] 195 | fn test_macro_in_macro7() { 196 | ascent! { 197 | relation foo(i32, i32) = vec![(0, 1), (1, 2), (2, 3), (3, 4), (4, 6), (3, 7)]; 198 | relation bar(Option, i32) = vec![(Some(1), 2), (Some(2), 3), (None, 4)]; 199 | 200 | macro foo($x: expr, $y: expr) { foo($x, $y), } 201 | macro bar($x: ident, $y: expr) { bar(?Some($x), $y) } 202 | 203 | macro foo2($x: expr, $y: expr) { 204 | foo!($x, $y), let x = $x, for x2 in [1, 2], ((foo(x, x2), let y = $y, let _ = println!("{}", y)) | if true, for y in [$y, $y]), 205 | foo!(x + 0, y - 0), foo(x, y), foo!(x, y), 206 | let z = |x: i32| {x}, foo(z(*x), z(*y)) 207 | } 208 | 209 | relation baz(i32, i32); 210 | macro baz($x: expr, $y: expr) {baz($x, $y)} 211 | relation baz_e(i32, i32); 212 | 213 | baz!(x, z) <-- bar!(x, y), foo!(y, z); 214 | baz!(a, c) <-- bar!(a, b), foo!(b, c); 215 | baz_e(x, z) <-- bar(?Some(x), y), foo(y, z); 216 | 217 | relation quax(i32, i32); 218 | relation quax_e(i32, i32); 219 | 220 | quax(x, z) <-- foo2!(x, y), foo2!(y, z); 221 | quax_e(x, z) <-- foo(x, y), foo(y, z); 222 | } 223 | 224 | let mut prog = AscentProgram::default(); 225 | prog.run(); 226 | 227 | println!("baz : {:?}", prog.baz); 228 | assert_rels_eq!(prog.baz, prog.baz_e); 229 | 230 | println!("quax: {:?}", prog.quax); 231 | assert_rels_eq!(prog.quax, prog.quax_e); 232 | } 233 | 234 | #[test] 235 | fn test_macro_in_macro8() { 236 | macro_rules! id { 237 | ($($inp: tt)*) => { $($inp)* }; 238 | } 239 | 240 | ascent! { 241 | relation foo(i32, i32) = vec![(0, 1), (1, 2), (2, 3), (3, 4), (4, 6), (3, 7)]; 242 | relation bar(Option, i32) = vec![(Some(1), 2), (Some(2), 3), (None, 4)]; 243 | 244 | macro foo2($x: expr, $y: expr) { foo($x, $y), } 245 | macro bar($x: ident, $y: expr) { bar(?Some($x), $y) } 246 | 247 | macro foo($xx: expr, $yy: expr) { 248 | foo2!($xx, $yy), let x = id!($xx), let y = id!($yy), for x2 in [1, 2], 249 | let _ = assert!(x == $xx && y == $yy), 250 | foo2!(id!(id!(x) + 0), id!(y - 0)), foo(x, y), foo2!(x, y), 251 | let z = id!(|x: i32| {x}), foo(z(*x), z(*y)) 252 | } 253 | 254 | relation baz(i32, i32); 255 | relation baz_e(i32, i32); 256 | baz(x, z) <-- bar!(x, y), foo!(y, z); 257 | 258 | relation baz2(i32, i32); 259 | baz2(a, c) <-- bar!(a, b), foo!(b, c); 260 | 261 | baz_e(x, z) <-- bar(?Some(x), y), foo(y, z); 262 | } 263 | 264 | let mut prog = AscentProgram::default(); 265 | prog.run(); 266 | 267 | println!("baz : {:?}", prog.baz); 268 | assert_rels_eq!(&prog.baz, &prog.baz_e); 269 | assert_rels_eq!(prog.baz2, prog.baz_e); 270 | } 271 | -------------------------------------------------------------------------------- /ascent_tests/src/se.rs: -------------------------------------------------------------------------------- 1 | #![allow(warnings)] 2 | use std::rc::Rc; 3 | 4 | use ascent::*; 5 | 6 | type SrcLine = u32; 7 | 8 | type Register = &'static str; 9 | 10 | #[derive(Clone, Copy, PartialEq, Eq, Hash)] 11 | pub enum Val { 12 | Ref(Register), 13 | Lit(i32), 14 | } 15 | 16 | #[derive(Clone, PartialEq, Eq, Hash)] 17 | pub enum Trace { 18 | Null, 19 | Cons(SrcLine, Rc), 20 | } 21 | 22 | #[derive(Clone, Copy, PartialEq, Eq, Hash)] 23 | pub enum Instr { 24 | Mov(Register, Val), 25 | Cmp(Register, Val, Val), 26 | Brz(Register, SrcLine), 27 | Add(Register, Val, Val), 28 | Br(SrcLine), 29 | } 30 | fn instr_vals(instr: &Instr) -> Vec<&Val> { todo!() } 31 | 32 | use Instr::*; 33 | ascent! { 34 | relation trace(SrcLine, Trace); //scrline duplicates the head of trace for fast lookup 35 | relation source(SrcLine, Instr); 36 | relation store(Trace, Register, i32); 37 | relation aeval(Trace, Val, i32); 38 | 39 | aeval(time.clone(), val, eval) <-- 40 | source(pc, instr), 41 | trace(pc, time), 42 | for (&val, &eval) in instr_vals(instr).into_iter().filter_map(|v| match v {Val::Lit(l) => Some((v,l)), Val::Ref(r) => None}); 43 | 44 | aeval(time.clone(), val, eval) <-- 45 | source(pc, instr), 46 | trace(pc, time), 47 | for (&val, ®) in instr_vals(instr).into_iter().filter_map(|v| match v {Val::Lit(x) => None, Val::Ref(r) => Some((v, r))}), 48 | store(time, reg, eval); 49 | 50 | trace(pc + 1, Trace::Cons(pc + 1, Rc::new(time.clone()) )) <-- 51 | source(pc, ?Mov(target, val)), 52 | trace(pc, time), 53 | aeval(time, val, eval); 54 | 55 | store(Trace::Cons(pc + 1, Rc::new(time.clone())), target, *target_eval) <-- 56 | source(pc, ?Mov(target, val)), 57 | trace(pc, time), 58 | aeval(time, val, target_eval); 59 | } 60 | -------------------------------------------------------------------------------- /ascent_tests/src/utils.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | use std::collections::HashSet; 3 | 4 | pub fn collect_set(iter: impl Iterator) -> HashSet { iter.collect() } 5 | 6 | pub fn into_set(iter: impl IntoIterator) -> HashSet { iter.into_iter().collect() } 7 | 8 | pub fn rels_equal( 9 | rel1: impl IntoIterator, rel2: impl IntoIterator, 10 | ) -> bool { 11 | rel1.into_iter().collect::>() == rel2.into_iter().collect::>() 12 | } 13 | 14 | #[macro_export] 15 | macro_rules! assert_rels_eq { 16 | ($rel1: expr, $rel2: expr) => { 17 | let (rel1, rel2) = ($rel1.into_iter().collect::>(), $rel2.into_iter().collect::>()); 18 | if rel1 != rel2 { 19 | panic!("Expected rels to be equal. \nrel1: {:?} \nrel2: {:?}", 20 | rel1, rel2); 21 | } 22 | }; 23 | } 24 | -------------------------------------------------------------------------------- /byods/README.md: -------------------------------------------------------------------------------- 1 | This directory contains custom data structure providers for Ascent's 2 | ["Bring Your Own Data Strcutures"](https://dl.acm.org/doi/10.1145/3622840) feature. 3 | 4 | To get started with custom data structures in Ascent, see the example in `./ascent-byods-rels/examples/steensgaard`. -------------------------------------------------------------------------------- /byods/ascent-byods-rels/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ascent-byods-rels" 3 | version.workspace = true 4 | edition = "2021" 5 | authors = ["Arash Sahebolamri"] 6 | description = "data structures for Ascent relations, made possible by Ascent's BYODS feature" 7 | keywords = ["Datalog", "Ascent", "BYODS", "data-structures", "union-find"] 8 | categories = ["data-structures"] 9 | homepage = "https://s-arash.github.io/ascent/" 10 | repository = "https://github.com/s-arash/ascent" 11 | license = "MIT" 12 | readme = "../../README.MD" 13 | autoexamples = false 14 | 15 | [dependencies] 16 | ascent = { workspace = true, default-features = false } 17 | derive_more = "0.99.17" 18 | itertools = "0.13" 19 | rustc-hash = "2.0" 20 | # syn is a dependency of derive_more 0.99, and the minimum version is buggy, so choosing a well-behaved 21 | # version. Should be removed when derive_more switches to syn v2.0. 22 | syn = "1.0.109" 23 | paste = "1.0" 24 | hashbrown = { version = "0.14", features = ["raw"] } 25 | 26 | [dev-dependencies] 27 | proptest = "1" 28 | rand = "0.8" 29 | serde = "1.0.193" 30 | separator = "0.4.1" 31 | 32 | [features] 33 | default = ["par"] 34 | compact = [] 35 | par = ["ascent/par", "hashbrown/rayon"] 36 | -------------------------------------------------------------------------------- /byods/ascent-byods-rels/examples/Cargo.toml: -------------------------------------------------------------------------------- 1 | # This (separate Cargo.toml) file is needed because cargo release would not work in dryrun mode otherwise 2 | 3 | [workspace] 4 | 5 | [package] 6 | name = "ascent-byods-rels-examples" 7 | version = "0.0.1" 8 | edition = "2021" 9 | publish = false 10 | autobins = false 11 | 12 | [dependencies] 13 | ascent = { path = "../../../ascent" } 14 | ascent-byods-rels = { path = ".." } 15 | itertools = "0.13" 16 | serde = "1.0" 17 | csv = "1.0" 18 | separator = "0.4.1" 19 | 20 | [[bin]] 21 | name = "steensgaard" 22 | path = "steensgaard/main.rs" 23 | 24 | [[bin]] 25 | name = "blockchain" 26 | path = "blockchain.rs" 27 | 28 | [[bin]] 29 | name = "bench_trrel" 30 | path = "bench_trrel.rs" 31 | 32 | [[bin]] 33 | name = "bench_trrel_uf" 34 | path = "bench_trrel_uf.rs" -------------------------------------------------------------------------------- /byods/ascent-byods-rels/examples/bench_trrel.rs: -------------------------------------------------------------------------------- 1 | use ascent::ascent; 2 | use ascent_byods_rels::trrel; 3 | use separator::Separatable; 4 | use std::time::Instant; 5 | 6 | ascent! { 7 | struct TrRelTest; 8 | 9 | #[ds(crate::trrel)] 10 | relation tr(u32, u32); 11 | 12 | relation inp(u32, u32); 13 | 14 | tr(x, y) <-- inp(x, y); 15 | 16 | relation tr_materialized(u32, u32); 17 | // tr_materialized(x, y) <-- tr(x, y); 18 | } 19 | 20 | ascent! { 21 | struct TrRelTestExplicitOpt; 22 | 23 | relation inp(u32, u32); 24 | 25 | relation tr_explicit(u32, u32); 26 | 27 | tr_explicit(x, z) <-- inp(x, y), tr_explicit(y, z); 28 | tr_explicit(x, y) <-- inp(x, y); 29 | } 30 | 31 | ascent! { 32 | struct TrRelTestExplicit; 33 | 34 | relation tr_explicit(u32, u32); 35 | tr_explicit(x, z) <-- tr_explicit(x, y), tr_explicit(y, z); 36 | 37 | relation inp(u32, u32); 38 | 39 | tr_explicit(x, y) <-- inp(x, y); 40 | } 41 | 42 | fn main() { 43 | for inp_size in [1000, 10000] { 44 | println!("\ninp size: {}", inp_size); 45 | 46 | let before = Instant::now(); 47 | let mut tr_prog = TrRelTest::default(); 48 | tr_prog.inp = (0..inp_size).map(|x| (x, x + 1)).collect(); 49 | tr_prog.run(); 50 | println!("tr took {}ms", before.elapsed().as_millis().separated_string()); 51 | 52 | let before = Instant::now(); 53 | let mut tr_explicit_opt_prog = TrRelTestExplicitOpt::default(); 54 | tr_explicit_opt_prog.inp = (0..inp_size).map(|x| (x, x + 1)).collect(); 55 | tr_explicit_opt_prog.run(); 56 | println!("tr_explicit_opt took {}ms", before.elapsed().as_millis().separated_string()); 57 | 58 | // let before = Instant::now(); 59 | // let mut tr_explicit_prog = TrRelTestExplicit::default(); 60 | // tr_explicit_prog.inp = (0..inp_size).map(|x| (x, x + 1)).collect(); 61 | // tr_explicit_prog.run(); 62 | // println!("tr_explicit took {}ms", before.elapsed().as_millis().separated_string()); 63 | 64 | println!("tr_explicit len: {}", tr_explicit_opt_prog.tr_explicit.len()); 65 | println!("tr len: {}", tr_prog.tr.len()); 66 | println!("tr_materialized len: {}", tr_prog.tr_materialized.len()); 67 | println!("tr_materialized: {:?}", tr_prog.tr_materialized); 68 | 69 | println!("========================"); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /byods/ascent-byods-rels/examples/bench_trrel_uf.rs: -------------------------------------------------------------------------------- 1 | mod tracking_alloc; 2 | 3 | use ascent::ascent; 4 | use ascent_byods_rels::trrel_uf; 5 | use itertools::Itertools; 6 | use tracking_alloc::TrackingAllocator; 7 | use std::alloc::System; 8 | use std::time::Instant; 9 | 10 | #[global_allocator] 11 | static GLOBAL: TrackingAllocator = TrackingAllocator(System); 12 | 13 | ascent! { 14 | struct TrRelUFTest; 15 | 16 | relation inp(u32, u32); 17 | 18 | #[ds(trrel_uf)] 19 | relation tr(u32, u32); 20 | 21 | tr(x, y) <-- inp(x, y); 22 | 23 | relation tr_materialized(u32, u32); 24 | // tr_materialized(x, y) <-- tr(x, y); 25 | } 26 | 27 | ascent! { 28 | struct TrRelUFTestExplicit; 29 | 30 | relation inp(u32, u32); 31 | 32 | relation tr_explicit(u32, u32); 33 | relation tr_explicit_proto(u32, u32); 34 | 35 | tr_explicit_proto(x, y) <-- inp(x, y); 36 | 37 | tr_explicit(x, y) <-- tr_explicit_proto(x, y); 38 | tr_explicit(x, z) <-- tr_explicit_proto(x, y), tr_explicit(y, z); 39 | } 40 | 41 | fn main() { 42 | let test_cases = vec![ 43 | ((0..5000).map(|x| (x, x + 1)).collect_vec(), "chain 5000"), 44 | ((0..5000).map(|x| (x, x + 1)).chain([(5000, 0)]).collect_vec(), "loop 5000"), 45 | ( 46 | (0..20) 47 | .flat_map(|x| { 48 | let start = x * 500; 49 | (start..start + 500).map(|x| (x, x + 1)).chain([(start + 500, start)]) 50 | }) 51 | .collect_vec(), 52 | "loops", 53 | ), 54 | ]; 55 | 56 | for (inp, name) in test_cases { 57 | println!("\n======= {name} ======="); 58 | 59 | tracking_alloc::reset_max_alloc(); 60 | let mem_use_before = tracking_alloc::current_alloc(); 61 | let mut prog = TrRelUFTest::default(); 62 | prog.inp = inp.clone(); 63 | let before = Instant::now(); 64 | prog.run(); 65 | println!("tr_materialized len: {}", prog.tr_materialized.len()); 66 | println!("tr with union find count exact: {}", prog.__tr_ind_common.count_exact()); 67 | println!("tr with union find took {:?}", before.elapsed()); 68 | println!("tr with union find max mem use: {}MB", (tracking_alloc::max_alloc() - mem_use_before) / (1 << 20)); 69 | 70 | println!("----------------"); 71 | tracking_alloc::reset_max_alloc(); 72 | let mem_use_before = tracking_alloc::current_alloc(); 73 | let mut explicit_prog = TrRelUFTestExplicit::default(); 74 | explicit_prog.inp = inp.clone(); 75 | let before = Instant::now(); 76 | explicit_prog.run(); 77 | println!("tr_explicit len: {}", explicit_prog.tr_explicit.len()); 78 | println!("tr explicit took {:?}", before.elapsed()); 79 | println!("tr explicit max mem use: {}MB", (tracking_alloc::max_alloc() - mem_use_before) / (1 << 20)); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /byods/ascent-byods-rels/examples/blockchain.rs: -------------------------------------------------------------------------------- 1 | mod tracking_alloc; 2 | 3 | use std::alloc::System; 4 | use std::time::Instant; 5 | 6 | use ascent::ascent; 7 | use ascent_byods_rels::eqrel; 8 | use itertools::Itertools; 9 | use tracking_alloc::TrackingAllocator; 10 | 11 | type User = u32; 12 | type Tx = u32; 13 | 14 | #[global_allocator] 15 | static GLOBAL: TrackingAllocator = TrackingAllocator(System); 16 | 17 | ascent! { 18 | struct BlockChainAnalysis; 19 | 20 | relation transaction(Tx, User); 21 | 22 | #[ds(eqrel)] 23 | relation same_user(User, User); 24 | 25 | same_user(u1, u2) <-- 26 | transaction(tx, u1), 27 | transaction(tx, u2); 28 | 29 | relation same_user_materialized(User, User); 30 | // same_user_materialized(u1, u2) <-- same_user(u1, u2); 31 | 32 | // same_user(y, y) <-- same_user(&42, y); 33 | // same_user(x, z) <-- same_user(x, y), same_user(y, z); 34 | } 35 | 36 | ascent! { 37 | struct BlockChainAnalysisExplicit; 38 | 39 | relation transaction(Tx, User); 40 | relation same_user_explicit(User, User); 41 | 42 | same_user_explicit(u1, u2) <-- 43 | transaction(tx, u1), 44 | transaction(tx, u2); 45 | 46 | // symmetry, reflexivity: 47 | same_user_explicit(u2, u1), 48 | same_user_explicit(u1, u1) <-- same_user_explicit(u1, u2); 49 | 50 | //transitivity: 51 | same_user_explicit(u1, u3) <-- same_user_explicit(u1, u2), same_user_explicit(u2, u3); 52 | } 53 | 54 | fn mem_pretty(bytes: usize) -> String { 55 | format!("{}KiB", bytes / 1024) 56 | } 57 | 58 | fn main() { 59 | 60 | for count in [100, 200, 400, 600, 800, 1200, 1600, 2000] { 61 | println!("\n=========== COUNT: {count} ============"); 62 | let mem_use_before = tracking_alloc::current_alloc(); 63 | let transaction = (0..count).flat_map(|tx| (tx..(tx + 2).min(count)).map(move |u| (tx, u))).collect_vec(); 64 | 65 | let mut prog = BlockChainAnalysis::default(); 66 | prog.transaction = transaction.iter().cloned().collect(); 67 | 68 | let before = Instant::now(); 69 | prog.run(); 70 | println!("prog took {:?} s", before.elapsed().as_secs_f32()); 71 | // println!("same_user len: {}", prog.same_user.len()); 72 | println!("same_user_count_exact: {}", prog.__same_user_ind_common.count_exact()); 73 | println!("same_user_materialized len: {}", prog.same_user_materialized.len()); 74 | println!("mem use by eqrel version: {}", mem_pretty(tracking_alloc::current_alloc() - mem_use_before)); 75 | 76 | println!("----------------"); 77 | 78 | let mut prog_explicit = BlockChainAnalysisExplicit::default(); 79 | prog_explicit.transaction = transaction.iter().cloned().collect(); 80 | 81 | let before = Instant::now(); 82 | let mem_use_before = tracking_alloc::current_alloc(); 83 | prog_explicit.run(); 84 | println!("prog_explicit took {:?} s", before.elapsed().as_secs_f32()); 85 | println!("same_user_explicit len: {}", prog_explicit.same_user_explicit.len()); 86 | println!("mem use by explicit version: {}", mem_pretty(tracking_alloc::current_alloc() - mem_use_before)); 87 | 88 | // println!("same_user_explicit: {:?}", prog.same_user_explicit); 89 | } 90 | println!("\nmax mem use: {}", tracking_alloc::max_alloc()); 91 | } 92 | -------------------------------------------------------------------------------- /byods/ascent-byods-rels/examples/steensgaard/main.rs: -------------------------------------------------------------------------------- 1 | #[path ="../tracking_alloc.rs"] 2 | mod tracking_alloc; 3 | 4 | use std::alloc::System; 5 | use std::borrow::Borrow; 6 | 7 | use ascent::ascent; 8 | use ascent::internal::Instant; 9 | use itertools::Itertools; 10 | 11 | use ascent_byods_rels::eqrel; 12 | use tracking_alloc::TrackingAllocator; 13 | 14 | #[global_allocator] 15 | static GLOBAL: TrackingAllocator = TrackingAllocator(System); 16 | 17 | type Symbol = &'static str; 18 | 19 | // examples adapted from Souffle's eqrel paper (https://souffle-lang.github.io/pdf/pact19.pdf) 20 | ascent! { 21 | #![measure_rule_times] 22 | /// Steensgaard analysis using `eqrel` 23 | struct Steensgaard; 24 | 25 | relation alloc(Symbol, Symbol); 26 | // x := y; 27 | relation assign(Symbol, Symbol); 28 | // x := y.f; 29 | relation load(Symbol, Symbol, Symbol); 30 | // x.f := y; 31 | relation store(Symbol, Symbol, Symbol); 32 | 33 | #[ds(eqrel)] 34 | relation vpt(Symbol, Symbol); 35 | 36 | // assignments 37 | vpt(x,y) <-- 38 | assign(x,y); 39 | 40 | // allocation sites 41 | vpt(x,y) <-- 42 | alloc(x,y); 43 | 44 | // load/store pairs 45 | vpt(y,p) <-- 46 | store(x,f, y), 47 | load(p,q,f), 48 | vpt(x,q); 49 | } 50 | 51 | ascent! { 52 | #![measure_rule_times] 53 | /// Explicit Steensgaard analysis 54 | struct SteensgaardExplicit; 55 | 56 | relation alloc(Symbol, Symbol); 57 | // x := y; 58 | relation assign(Symbol, Symbol); 59 | // x := y.f; 60 | relation load(Symbol, Symbol, Symbol); 61 | // x.f := y; 62 | relation store(Symbol, Symbol, Symbol); 63 | 64 | relation vpt(Symbol, Symbol); 65 | 66 | vpt(y, x), vpt(x, x) <-- vpt(x, y); 67 | vpt(x, z) <-- vpt(x, y), vpt(y, z); 68 | 69 | // assignments 70 | vpt(x,y) <-- 71 | assign(x,y); 72 | 73 | // allocation sites 74 | vpt(x,y) <-- 75 | alloc(x,y); 76 | 77 | // load/store pairs 78 | vpt(y,p) <-- 79 | store(x,f, y), 80 | load(p,q,f), 81 | vpt(x,q); 82 | } 83 | 84 | fn read_csv(path: &str) -> impl Iterator 85 | where 86 | for<'de> T: serde::de::Deserialize<'de>, 87 | { 88 | csv::ReaderBuilder::new() 89 | .delimiter(b'\t') 90 | .has_headers(false) 91 | .double_quote(false) 92 | .quoting(false) 93 | .from_path(path) 94 | .unwrap() 95 | .into_deserialize() 96 | .map(|x| x.unwrap()) 97 | } 98 | 99 | fn main() { 100 | let path = "./steensgaard/openjdk_javalang_steensgaard/"; 101 | let get_path = |x: &str| format!("{path}{x}"); 102 | 103 | println!("Running eqrel version."); 104 | let mem_use_before = tracking_alloc::current_alloc(); 105 | let start_time = Instant::now(); 106 | let mut prog = Steensgaard::default(); 107 | 108 | prog.alloc = read_csv::<(String, String)>(&get_path("alloc.facts")) 109 | .map(|(x, y)| (leak(x), leak(y))) 110 | .collect_vec(); 111 | prog.assign = read_csv::<(String, String)>(&get_path("assign.facts")) 112 | .map(|(x, y)| (leak(x), leak(y))) 113 | .collect_vec(); 114 | prog.load = read_csv::<(String, String, String)>(&get_path("load.facts")) 115 | .map(|(x, y, z)| (leak(x), leak(y), leak(z))) 116 | .collect_vec(); 117 | prog.store = read_csv::<(String, String, String)>(&get_path("store.facts")) 118 | .map(|(x, y, z)| (leak(x), leak(y), leak(z))) 119 | .collect_vec(); 120 | 121 | prog.run(); 122 | 123 | println!("mem use: {:.2} Mib", (tracking_alloc::max_alloc() - mem_use_before) as f64 / 2f64.powi(20)); 124 | println!("everything took: {:?}", start_time.elapsed()); 125 | println!("vpt size: {}", prog.__vpt_ind_common.count_exact()); 126 | 127 | tracking_alloc::reset_max_alloc(); 128 | 129 | // Explicit version: 130 | println!(""); 131 | println!("Running Explicit version. This will take FOREVER!"); 132 | let mem_use_before = tracking_alloc::current_alloc(); 133 | let start_time = Instant::now(); 134 | let mut prog = SteensgaardExplicit::default(); 135 | 136 | prog.alloc = read_csv::<(String, String)>(&get_path("alloc.facts")) 137 | .map(|(x, y)| (leak(x), leak(y))) 138 | .collect_vec(); 139 | prog.assign = read_csv::<(String, String)>(&get_path("assign.facts")) 140 | .map(|(x, y)| (leak(x), leak(y))) 141 | .collect_vec(); 142 | prog.load = read_csv::<(String, String, String)>(&get_path("load.facts")) 143 | .map(|(x, y, z)| (leak(x), leak(y), leak(z))) 144 | .collect_vec(); 145 | prog.store = read_csv::<(String, String, String)>(&get_path("store.facts")) 146 | .map(|(x, y, z)| (leak(x), leak(y), leak(z))) 147 | .collect_vec(); 148 | 149 | prog.run(); 150 | 151 | println!("mem use: {:.2} Mib", (tracking_alloc::max_alloc() - mem_use_before) as f64 / 2f64.powi(20)); 152 | println!("everything took: {:?}", start_time.elapsed()); 153 | println!("vpt size: {}", prog.vpt.len()); 154 | } 155 | 156 | fn leak + 'static, TB: ?Sized>(x: T) -> &'static TB { 157 | let leaked: &'static T = Box::leak(Box::new(x)); 158 | leaked.borrow() 159 | } 160 | -------------------------------------------------------------------------------- /byods/ascent-byods-rels/examples/tracking_alloc.rs: -------------------------------------------------------------------------------- 1 | use std::alloc::GlobalAlloc; 2 | 3 | 4 | /// An allocator that keeps track of the currently-allocated memory 5 | /// (in globals `MAX_ALLOC` and `CURRENT_ALLOC`) 6 | #[derive(Default)] 7 | pub struct TrackingAllocator(pub Alloc); 8 | 9 | pub static mut MAX_ALLOC: usize = 0; 10 | pub static mut CURRENT_ALLOC: usize = 0; 11 | 12 | unsafe impl GlobalAlloc for TrackingAllocator { 13 | 14 | #[inline] 15 | unsafe fn alloc(&self, layout: std::alloc::Layout) -> *mut u8 { 16 | CURRENT_ALLOC += layout.size(); 17 | MAX_ALLOC = MAX_ALLOC.max(CURRENT_ALLOC); 18 | self.0.alloc(layout) 19 | } 20 | 21 | #[inline] 22 | unsafe fn dealloc(&self, ptr: *mut u8, layout: std::alloc::Layout) { 23 | CURRENT_ALLOC = CURRENT_ALLOC.saturating_sub(layout.size()); 24 | self.0.dealloc(ptr, layout) 25 | } 26 | 27 | #[inline] 28 | unsafe fn alloc_zeroed(&self, layout: std::alloc::Layout) -> *mut u8 { 29 | CURRENT_ALLOC += layout.size(); 30 | MAX_ALLOC = MAX_ALLOC.max(CURRENT_ALLOC); 31 | self.0.alloc_zeroed(layout) 32 | } 33 | 34 | #[inline] 35 | unsafe fn realloc(&self, ptr: *mut u8, layout: std::alloc::Layout, new_size: usize) -> *mut u8 { 36 | if new_size > layout.size() { 37 | CURRENT_ALLOC += new_size - layout.size(); 38 | MAX_ALLOC = MAX_ALLOC.max(CURRENT_ALLOC); 39 | } else { 40 | CURRENT_ALLOC = CURRENT_ALLOC.saturating_sub(layout.size() - new_size); 41 | } 42 | self.0.realloc(ptr, layout, new_size) 43 | } 44 | } 45 | 46 | pub fn current_alloc() -> usize { 47 | unsafe { CURRENT_ALLOC } 48 | } 49 | 50 | pub fn max_alloc() -> usize { 51 | unsafe { MAX_ALLOC } 52 | } 53 | 54 | #[allow(dead_code)] 55 | pub fn reset_max_alloc() { 56 | unsafe { 57 | MAX_ALLOC = CURRENT_ALLOC; 58 | } 59 | } -------------------------------------------------------------------------------- /byods/ascent-byods-rels/src/adaptor/bin_rel_plus_ternary_provider.rs: -------------------------------------------------------------------------------- 1 | // Re-export macros in this module for your binary relation data structure provider 2 | // that you wish to be a ternary relation as well 3 | 4 | #[doc(hidden)] 5 | #[macro_export] 6 | macro_rules! bin_rel_plus_ternary_provider_rel { 7 | ($name: ident, ($col0: ty, $col1: ty), $indices: expr, ser, ()) => { 8 | $crate::fake_vec::FakeVec<($col0, $col1)> 9 | }; 10 | 11 | ($name: ident, ($col0: ty, $col1: ty, $col2: ty), $indices: expr, ser, ()) => { 12 | $crate::fake_vec::FakeVec<($col0, $col1, $col2)> 13 | }; 14 | } 15 | pub use bin_rel_plus_ternary_provider_rel as rel; 16 | 17 | #[doc(hidden)] 18 | #[macro_export] 19 | macro_rules! bin_rel_plus_ternary_provider_full_ind { 20 | ($name: ident, ($col0: ty, $col1: ty), $indices: expr, ser, (), $key: ty, $val: ty) => { 21 | $crate::adaptor::bin_rel::ToByodsBinRelInd0_1<$col0, $col1> 22 | }; 23 | 24 | ($name: ident, ($col0: ty, $col1: ty, $col2: ty), $indices: expr, ser, (), $key: ty, $val: ty) => { 25 | $crate::adaptor::bin_rel_to_ternary::ToBinRelToTernaryInd0_1_2<$col0, $col1, $col2> 26 | }; 27 | } 28 | pub use bin_rel_plus_ternary_provider_full_ind as rel_full_ind; 29 | 30 | #[doc(hidden)] 31 | #[macro_export] 32 | macro_rules! bin_rel_plus_ternary_provider_ind { 33 | ($name: ident, ($col0: ty, $col1: ty), $indices: expr, ser, (), [0], $key: ty, $val: ty) => { 34 | $crate::adaptor::bin_rel::ToByodsBinRelInd0<$col0, $col1> 35 | }; 36 | ($name: ident, ($col0: ty, $col1: ty), $indices: expr, ser, (), [1], $key: ty, $val: ty) => { 37 | $crate::adaptor::bin_rel::ToByodsBinRelInd1<$col0, $col1> 38 | }; 39 | ($name: ident, ($col0: ty, $col1: ty), $indices: expr, ser, (), [], $key: ty, $val: ty) => { 40 | $crate::adaptor::bin_rel::ToByodsBinRelIndNone<$col0, $col1> 41 | }; 42 | 43 | // ternary 44 | ($name: ident, ($col0: ty, $col1: ty, $col2: ty), $indices: expr, ser, (), [], $key: ty, $val: ty) => { 45 | $crate::adaptor::bin_rel_to_ternary::ToBinRelToTernaryIndNone<$col0, $col1, $col2> 46 | }; 47 | ($name: ident, ($col0: ty, $col1: ty, $col2: ty), $indices: expr, ser, (), [0], $key: ty, $val: ty) => { 48 | $crate::adaptor::bin_rel_to_ternary::ToBinRelToTernaryInd0<$col0, $col1, $col2> 49 | }; 50 | ($name: ident, ($col0: ty, $col1: ty, $col2: ty), $indices: expr, ser, (), [1], $key: ty, $val: ty) => { 51 | $crate::adaptor::bin_rel_to_ternary::ToBinRelToTernaryInd1<$col0, $col1, $col2> 52 | }; 53 | ($name: ident, ($col0: ty, $col1: ty, $col2: ty), $indices: expr, ser, (), [2], $key: ty, $val: ty) => { 54 | $crate::adaptor::bin_rel_to_ternary::ToBinRelToTernaryInd2<$col0, $col1, $col2> 55 | }; 56 | ($name: ident, ($col0: ty, $col1: ty, $col2: ty), $indices: expr, ser, (), [0, 1], $key: ty, $val: ty) => { 57 | $crate::adaptor::bin_rel_to_ternary::ToBinRelToTernaryInd0_1<$col0, $col1, $col2> 58 | }; 59 | ($name: ident, ($col0: ty, $col1: ty, $col2: ty), $indices: expr, ser, (), [0, 2], $key: ty, $val: ty) => { 60 | $crate::adaptor::bin_rel_to_ternary::ToBinRelToTernaryInd0_2<$col0, $col1, $col2> 61 | }; 62 | ($name: ident, ($col0: ty, $col1: ty, $col2: ty), $indices: expr, ser, (), [1, 2], $key: ty, $val: ty) => { 63 | $crate::adaptor::bin_rel_to_ternary::ToBinRelToTernaryInd1_2<$col0, $col1, $col2> 64 | }; 65 | ($name: ident, ($col0: ty, $col1: ty, $col2: ty), $indices: expr, ser, (), [0, 1, 2], $key: ty, $val: ty) => { 66 | $crate::adaptor::bin_rel_to_ternary::ToBinRelToTernaryInd0_1_2<$col0, $col1, $col2> 67 | }; 68 | } 69 | pub use bin_rel_plus_ternary_provider_ind as rel_ind; 70 | 71 | #[doc(hidden)] 72 | #[macro_export] 73 | macro_rules! bin_rel_plus_ternary_provider_rel_codegen { 74 | ( $($tt: tt)* ) => { }; 75 | } 76 | pub use bin_rel_plus_ternary_provider_rel_codegen as rel_codegen; 77 | 78 | #[cfg(test)] 79 | mod test { 80 | #[doc(hidden)] 81 | #[macro_export] 82 | macro_rules! bin_rel_plus_ternary_provider_ind_common { 83 | ($name: ident, ($col0: ty, $col1: ty), $indices: tt, ser, ()) => { 84 | $crate::adaptor::bin_rel_provider::test::DummyRel<$col0, $col1> 85 | }; 86 | 87 | ($name: ident, ($col0: ty, $col1: ty, $col2: ty), $indices: tt, ser, ()) => { 88 | $crate::adaptor::bin_rel_to_ternary::BinRelToTernaryWrapper< 89 | // reverse_map_1 required: 90 | {$crate::inds_contain!($indices, [1]) || $crate::inds_contain!($indices, [1, 2])}, 91 | // reverse_map_2 required: 92 | {$crate::inds_contain!($indices, [2]) || $crate::inds_contain!($indices, [1, 2])}, 93 | $col0, $col1, $col2, 94 | $crate::adaptor::bin_rel_provider::test::DummyRel<$col1, $col2> 95 | > 96 | }; 97 | } 98 | pub use bin_rel_plus_ternary_provider_ind_common as rel_ind_common; 99 | 100 | pub use super::{rel, rel_codegen, rel_full_ind, rel_ind}; 101 | 102 | ascent::ascent! { 103 | #[ds(self)] 104 | relation foo(u32, u64, u128); 105 | 106 | relation bar(u32, u64, u128); 107 | 108 | foo(*x as u32, *y as u64, *z as u128) <-- foo(y, x, z); 109 | 110 | bar(x, y, z) <-- foo(x, y, z), bar(x, _, z); 111 | 112 | bar(x, y, z) <-- foo(_, y, z), bar(x, y, z), foo(x, _, _); 113 | 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /byods/ascent-byods-rels/src/adaptor/bin_rel_provider.rs: -------------------------------------------------------------------------------- 1 | //! Re-export macros in this module for your binary relation data structure provider 2 | //! implementd via [`ByodsBinRel`](crate::adaptor::bin_rel::ByodsBinRel) 3 | #[doc(hidden)] 4 | #[macro_export] 5 | macro_rules! bin_rel_provider_rel { 6 | ($name: ident, ($col0: ty, $col1: ty), $indices: expr, ser, ()) => { 7 | $crate::fake_vec::FakeVec<($col0, $col1)> 8 | }; 9 | } 10 | pub use bin_rel_provider_rel as rel; 11 | 12 | #[doc(hidden)] 13 | #[macro_export] 14 | macro_rules! bin_rel_provider_full_ind { 15 | ($name: ident, ($col0: ty, $col1: ty), $indices: expr, ser, (), $key: ty, $val: ty) => { 16 | $crate::adaptor::bin_rel::ToByodsBinRelInd0_1<$col0, $col1> 17 | }; 18 | } 19 | pub use bin_rel_provider_full_ind as rel_full_ind; 20 | 21 | #[doc(hidden)] 22 | #[macro_export] 23 | macro_rules! bin_rel_provider_ind { 24 | ($name: ident, ($col0: ty, $col1: ty), $indices: expr, ser, (), [0], $key: ty, $val: ty) => { 25 | $crate::adaptor::bin_rel::ToByodsBinRelInd0<$col0, $col1> 26 | }; 27 | ($name: ident, ($col0: ty, $col1: ty), $indices: expr, ser, (), [1], $key: ty, $val: ty) => { 28 | $crate::adaptor::bin_rel::ToByodsBinRelInd1<$col0, $col1> 29 | }; 30 | ($name: ident, ($col0: ty, $col1: ty), $indices: expr, ser, (), [], $key: ty, $val: ty) => { 31 | $crate::adaptor::bin_rel::ToByodsBinRelIndNone<$col0, $col1> 32 | }; 33 | } 34 | pub use bin_rel_provider_ind as rel_ind; 35 | 36 | #[doc(hidden)] 37 | #[macro_export] 38 | macro_rules! bin_rel_provider_rel_codegen { 39 | ( $($tt: tt)* ) => { }; 40 | } 41 | pub use bin_rel_provider_rel_codegen as rel_codegen; 42 | 43 | pub(crate) mod test { 44 | use std::iter::Once; 45 | use std::marker::PhantomData; 46 | 47 | use ascent::internal::RelIndexMerge; 48 | 49 | use crate::adaptor::bin_rel::ByodsBinRel; 50 | 51 | #[doc(hidden)] 52 | #[macro_export] 53 | macro_rules! bin_rel_provider_ind_common { 54 | ($name: ident, ($col0: ty, $col1: ty), $indices: expr, ser, ()) => { 55 | $crate::adaptor::bin_rel_provider::test::DummyRel<$col0, $col1> 56 | }; 57 | } 58 | pub use bin_rel_provider_ind_common as rel_ind_common; 59 | 60 | pub use super::{rel, rel_codegen, rel_full_ind, rel_ind}; 61 | 62 | pub struct DummyRel(PhantomData<(T0, T1)>); 63 | 64 | impl Default for DummyRel { 65 | fn default() -> Self { Self(Default::default()) } 66 | } 67 | 68 | impl RelIndexMerge for DummyRel { 69 | fn move_index_contents(_from: &mut Self, _to: &mut Self) { todo!() } 70 | } 71 | 72 | impl ByodsBinRel for DummyRel { 73 | type T0 = T0; 74 | type T1 = T1; 75 | 76 | fn contains(&self, _x0: &Self::T0, _x1: &Self::T1) -> bool { todo!() } 77 | 78 | type AllIter<'a> 79 | = Once<(&'a T0, &'a T1)> 80 | where Self: 'a; 81 | fn iter_all(&self) -> Self::AllIter<'_> { todo!() } 82 | 83 | fn len_estimate(&self) -> usize { todo!() } 84 | 85 | type Ind0AllIterValsIter<'a> 86 | = Once<&'a Self::T1> 87 | where Self: 'a; 88 | type Ind0AllIter<'a> 89 | = Once<(&'a Self::T0, Self::Ind0AllIterValsIter<'a>)> 90 | where Self: 'a; 91 | fn ind0_iter_all(&self) -> Self::Ind0AllIter<'_> { todo!() } 92 | fn ind0_len_estimate(&self) -> usize { todo!() } 93 | 94 | type Ind0ValsIter<'a> 95 | = Once<&'a Self::T1> 96 | where Self: 'a; 97 | fn ind0_index_get<'a>(&'a self, _key: &Self::T0) -> Option> { todo!() } 98 | 99 | type Ind1AllIterValsIter<'a> 100 | = Once<&'a Self::T0> 101 | where Self: 'a; 102 | type Ind1AllIter<'a> 103 | = Once<(&'a Self::T1, Self::Ind1AllIterValsIter<'a>)> 104 | where Self: 'a; 105 | fn ind1_iter_all(&self) -> Self::Ind1AllIter<'_> { todo!() } 106 | fn ind1_len_estimate(&self) -> usize { todo!() } 107 | 108 | type Ind1ValsIter<'a> 109 | = Once<&'a Self::T0> 110 | where Self: 'a; 111 | fn ind1_index_get<'a>(&'a self, _key: &Self::T1) -> Option> { todo!() } 112 | fn insert(&mut self, _x0: Self::T0, _x1: Self::T1) -> bool { todo!() } 113 | } 114 | 115 | ascent::ascent! { 116 | #[ds(super::test)] 117 | relation foo(u32, usize); 118 | 119 | foo(*x as u32, *y as usize) <-- foo(y, x); 120 | foo(x, y) <-- foo(x, y), foo(& (*y as u32), (*x as usize)); 121 | 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /byods/ascent-byods-rels/src/adaptor/mod.rs: -------------------------------------------------------------------------------- 1 | //! Utilities for easily implementing custom data structures for binary relations 2 | pub mod bin_rel; 3 | pub mod bin_rel_provider; 4 | pub mod bin_rel_to_ternary; 5 | pub mod bin_rel_plus_ternary_provider; 6 | -------------------------------------------------------------------------------- /byods/ascent-byods-rels/src/binary_rel.rs: -------------------------------------------------------------------------------- 1 | use std::hash::{BuildHasherDefault, Hash}; 2 | 3 | use ascent::internal::{RelIndexRead, RelIndexReadAll}; 4 | use hashbrown::HashMap; 5 | use hashbrown::hash_map::Iter; 6 | use rustc_hash::FxHasher; 7 | 8 | pub type MyHashSetIter<'a, T> = hashbrown::hash_set::Iter<'a, T>; 9 | pub type MyHashSet = hashbrown::HashSet; 10 | 11 | pub type Map = HashMap>, BuildHasherDefault>; 12 | pub type RevMap = HashMap, BuildHasherDefault>; 13 | 14 | #[derive(Clone)] 15 | pub struct BinaryRel { 16 | pub(crate) map: Map, 17 | pub(crate) reverse_map: RevMap, 18 | } 19 | 20 | impl Default for BinaryRel { 21 | fn default() -> Self { Self { map: Default::default(), reverse_map: Default::default() } } 22 | } 23 | 24 | impl BinaryRel { 25 | /// returns true if this tuple did not exist in the binary relation 26 | pub fn insert(&mut self, x: T, y: T) -> bool { 27 | if self.map.entry(x.clone()).or_default().insert(y.clone()) { 28 | self.reverse_map.entry(y).or_default().push(x); 29 | true 30 | } else { 31 | false 32 | } 33 | } 34 | 35 | /// returns true if this tuple did not exist in the binary relation 36 | pub fn insert_by_ref(&mut self, x: &T, y: &T) -> bool { 37 | let added = match self.map.raw_entry_mut().from_key(x) { 38 | hashbrown::hash_map::RawEntryMut::Occupied(mut occ) => occ.get_mut().insert(y.clone()), 39 | hashbrown::hash_map::RawEntryMut::Vacant(vac) => { 40 | vac.insert(x.clone(), MyHashSet::from_iter([y.clone()])); 41 | true 42 | }, 43 | }; 44 | if added { 45 | match self.reverse_map.raw_entry_mut().from_key(y) { 46 | hashbrown::hash_map::RawEntryMut::Occupied(mut occ) => { 47 | occ.get_mut().push(x.clone()); 48 | }, 49 | hashbrown::hash_map::RawEntryMut::Vacant(vac) => { 50 | vac.insert(y.clone(), vec![x.clone()]); 51 | }, 52 | }; 53 | true 54 | } else { 55 | false 56 | } 57 | // if self.map.raw_entry_mut().from_key(x)..entry(x.clone()).or_default().insert(y.clone()) { 58 | // self.reverse_map.entry(y).or_default().push(x); 59 | // true 60 | // } else { 61 | // false 62 | // } 63 | } 64 | 65 | pub fn iter_all(&self) -> impl Iterator + '_ { 66 | self.map.iter().flat_map(|(x, x_set)| x_set.iter().map(move |y| (x, y))) 67 | } 68 | 69 | #[inline] 70 | pub fn contains(&self, x: &T, y: &T) -> bool { self.map.get(x).is_some_and(|s| s.contains(y)) } 71 | 72 | pub fn count_estimate(&self) -> usize { 73 | let sample_size = 3; 74 | let sum = self.map.values().take(sample_size).map(|x| x.len()).sum::(); 75 | sum * self.map.len() / sample_size.min(self.map.len()).max(1) 76 | } 77 | 78 | pub fn count_exact(&self) -> usize { self.map.values().map(|x| x.len()).sum() } 79 | } 80 | 81 | pub struct MapRelIndexAdaptor<'a, T: Clone + Hash + Eq>( 82 | pub &'a HashMap>, BuildHasherDefault>, 83 | ); 84 | 85 | impl<'a, T: Clone + Hash + Eq + 'a> RelIndexReadAll<'a> for MapRelIndexAdaptor<'a, T> { 86 | type Key = &'a T; 87 | 88 | type Value = &'a T; 89 | 90 | type ValueIteratorType = MyHashSetIter<'a, T>; 91 | 92 | type AllIteratorType = std::iter::Map< 93 | Iter<'a, T, MyHashSet>>, 94 | for<'aa> fn((&'aa T, &'a MyHashSet>)) -> (&'aa T, Self::ValueIteratorType), 95 | >; 96 | 97 | fn iter_all(&'a self) -> Self::AllIteratorType { 98 | let res: Self::AllIteratorType = self.0.iter().map(|(k, v)| { 99 | let v_iter = v.iter(); 100 | (k, v_iter) 101 | }); 102 | res 103 | } 104 | } 105 | 106 | impl<'a, T: Clone + Hash + Eq> RelIndexRead<'a> for MapRelIndexAdaptor<'a, T> { 107 | type Key = T; 108 | 109 | type Value = &'a T; 110 | 111 | type IteratorType = hashbrown::hash_set::Iter<'a, T>; 112 | 113 | fn index_get(&'a self, key: &Self::Key) -> Option { 114 | let set = self.0.get(key)?; 115 | let res = set.iter(); 116 | Some(res) 117 | } 118 | 119 | fn len_estimate(&'a self) -> usize { self.0.len() } 120 | fn is_empty(&'a self) -> bool { self.0.is_empty() } 121 | } 122 | 123 | pub struct RelIndexValTransformer { 124 | rel: T, 125 | f: F, 126 | } 127 | 128 | impl<'a, T: 'a, F: 'a, V: 'a, U: 'a> RelIndexRead<'a> for RelIndexValTransformer 129 | where 130 | T: RelIndexRead<'a, Value = V>, 131 | F: Fn(V) -> U, 132 | { 133 | type Key = T::Key; 134 | type Value = U; 135 | 136 | type IteratorType = std::iter::Map< 137 | std::iter::Zip>>, 138 | for<'aa> fn((V, &'aa RelIndexValTransformer)) -> U, 139 | >; 140 | 141 | fn index_get(&'a self, key: &Self::Key) -> Option { 142 | let res: Self::IteratorType = 143 | self.rel.index_get(key)?.zip(std::iter::repeat(self)).map(|(x, _self)| (_self.f)(x)); 144 | Some(res) 145 | } 146 | 147 | fn len_estimate(&'a self) -> usize { self.rel.len_estimate() } 148 | fn is_empty(&'a self) -> bool { self.rel.is_empty() } 149 | } 150 | 151 | impl<'a, T: 'a, F: 'a, V: 'a, U: 'a> RelIndexReadAll<'a> for RelIndexValTransformer 152 | where 153 | T: RelIndexReadAll<'a, Value = V>, 154 | F: Fn(V) -> U, 155 | { 156 | type Key = T::Key; 157 | type Value = U; 158 | 159 | type ValueIteratorType = std::iter::Map< 160 | std::iter::Zip>>, 161 | for<'aa> fn((V, &'aa RelIndexValTransformer)) -> U, 162 | >; 163 | 164 | type AllIteratorType = Box + 'a>; 165 | 166 | fn iter_all(&'a self) -> Self::AllIteratorType { 167 | let res = self.rel.iter_all().map(move |(k, vals_iter)| { 168 | let new_vals_iter: Self::ValueIteratorType = 169 | vals_iter.zip(std::iter::repeat(self)).map(|(x, _self)| (_self.f)(x)); 170 | (k, new_vals_iter) 171 | }); 172 | 173 | Box::new(res) 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /byods/ascent-byods-rels/src/eqrel.rs: -------------------------------------------------------------------------------- 1 | //! equivalence relations for Ascent 2 | 3 | #[doc(hidden)] 4 | #[macro_export] 5 | macro_rules! eqrel_rel_codegen { 6 | ( $($tt: tt)* ) => { }; 7 | } 8 | pub use eqrel_rel_codegen as rel_codegen; 9 | 10 | #[doc(hidden)] 11 | #[macro_export] 12 | macro_rules! eqrel_rel { 13 | ($name: ident, ($col1: ty, $col2: ty), $($rest: tt)*) => { 14 | $crate::eqrel_binary::rel!(($col1, $col2), $($rest)*) 15 | }; 16 | ($name: ident, ($col1: ty, $col2: ty, $col3: ty), $($rest: tt)*) => { 17 | $crate::eqrel_ternary::rel!(($col1, $col2, $col3), $($rest)*) 18 | }; 19 | } 20 | pub use eqrel_rel as rel; 21 | 22 | #[doc(hidden)] 23 | #[macro_export] 24 | macro_rules! eqrel_rel_full_ind { 25 | ($name: ident, ($col1: ty, $col2: ty), $($rest: tt)*) => { 26 | $crate::eqrel_binary::rel_full_ind!(($col1, $col2), $($rest)*) 27 | }; 28 | ($name: ident, ($col1: ty, $col2: ty, $col3: ty), $($rest: tt)*) => { 29 | $crate::eqrel_ternary::rel_full_ind!(($col1, $col2, $col3), $($rest)*) 30 | }; 31 | } 32 | pub use eqrel_rel_full_ind as rel_full_ind; 33 | 34 | #[doc(hidden)] 35 | #[macro_export] 36 | macro_rules! eqrel_rel_ind { 37 | ($name: ident, ($col1: ty, $col2: ty), $($rest: tt)*) => { 38 | $crate::eqrel_binary::rel_ind!(($col1, $col2), $($rest)*) 39 | }; 40 | ($name: ident, ($col1: ty, $col2: ty, $col3: ty), $($rest: tt)*) => { 41 | $crate::eqrel_ternary::rel_ind!(($col1, $col2, $col3), $($rest)*) 42 | }; 43 | } 44 | pub use eqrel_rel_ind as rel_ind; 45 | 46 | #[doc(hidden)] 47 | #[macro_export] 48 | macro_rules! eqrel_rel_ind_common { 49 | ($name: ident, ($col1: ty, $col2: ty), $($rest: tt)*) => { 50 | $crate::eqrel_binary::rel_ind_common!(($col1, $col2), $($rest)*) 51 | }; 52 | ($name: ident, ($col1: ty, $col2: ty, $col3: ty), $($rest: tt)*) => { 53 | $crate::eqrel_ternary::rel_ind_common!(($col1, $col2, $col3), $($rest)*) 54 | }; 55 | } 56 | pub use eqrel_rel_ind_common as rel_ind_common; 57 | -------------------------------------------------------------------------------- /byods/ascent-byods-rels/src/eqrel_binary.rs: -------------------------------------------------------------------------------- 1 | #[doc(hidden)] 2 | #[macro_export] 3 | macro_rules! eqrel_binary_rel { 4 | (($col1: ty, $col2: ty), $indices: expr, ser, ()) => { 5 | // ::std::vec::Vec<($col1, $col2)> 6 | $crate::fake_vec::FakeVec<($col1, $col2)> 7 | }; 8 | 9 | // par: 10 | (($col1: ty, $col2: ty), $indices: expr, par, ()) => { 11 | // ::std::vec::Vec<($col1, $col2)> 12 | $crate::fake_vec::FakeVec<($col1, $col2)> 13 | }; 14 | } 15 | pub use eqrel_binary_rel as rel; 16 | 17 | #[doc(hidden)] 18 | #[macro_export] 19 | macro_rules! eqrel_binary_rel_full_ind { 20 | (($col1: ty, $col2: ty), $indices: expr, ser, (), $key: ty, $val: ty) => { 21 | $crate::eqrel_ind::ToEqRelInd0_1<$col1> 22 | }; 23 | 24 | // par: 25 | (($col1: ty, $col2: ty), $indices: expr, par, (), $key: ty, $val: ty) => { 26 | $crate::ceqrel_ind::ToEqRelInd0_1<$col1> 27 | }; 28 | } 29 | pub use eqrel_binary_rel_full_ind as rel_full_ind; 30 | 31 | #[doc(hidden)] 32 | #[macro_export] 33 | macro_rules! eqrel_binary_rel_ind { 34 | (($col1: ty, $col2: ty), $indices: expr, ser, (), [0], $key: ty, $val: ty) => { 35 | $crate::eqrel_ind::ToEqRelInd0<$col1> 36 | }; 37 | (($col1: ty, $col2: ty), $indices: expr, ser, (), [1], $key: ty, $val: ty) => { 38 | $crate::eqrel_ind::ToEqRelInd0<$col1> 39 | }; 40 | (($col1: ty, $col2: ty), $indices: expr, ser, (), [], $key: ty, $val: ty) => { 41 | $crate::eqrel_ind::ToEqRelIndNone<$col1> 42 | }; 43 | 44 | // par: 45 | (($col1: ty, $col2: ty), $indices: expr, par, (), [0], $key: ty, $val: ty) => { 46 | $crate::ceqrel_ind::ToEqRelInd0<$col1> 47 | }; 48 | (($col1: ty, $col2: ty), $indices: expr, par, (), [1], $key: ty, $val: ty) => { 49 | $crate::ceqrel_ind::ToEqRelInd0<$col1> 50 | }; 51 | (($col1: ty, $col2: ty), $indices: expr, par, (), [], $key: ty, $val: ty) => { 52 | $crate::ceqrel_ind::ToEqRelIndNone<$col1> 53 | }; 54 | } 55 | pub use eqrel_binary_rel_ind as rel_ind; 56 | 57 | #[doc(hidden)] 58 | #[macro_export] 59 | macro_rules! eqrel_binary_rel_ind_common { 60 | (($col1: ty, $col2: ty), $indices: expr, ser, ()) => { 61 | $crate::eqrel_ind::EqRelIndCommon<$col1> 62 | }; 63 | 64 | // par: 65 | (($col1: ty, $col2: ty), $indices: expr, par, ()) => { 66 | $crate::ceqrel_ind::CEqRelIndCommon<$col1> 67 | }; 68 | } 69 | pub use eqrel_binary_rel_ind_common as rel_ind_common; 70 | 71 | fn _test_macros() { 72 | let _x: rel!((u32, u32), [[0, 1], [0]], ser, ()); 73 | let _full_ind: rel_full_ind!((u32, u32), [[0, 1], [0]], ser, (), (u32, u32), ()); 74 | let _ind_0: rel_ind!((u32, u32), [[0, 1], [0]], ser, (), [0], (u32,), (u32,)); 75 | } 76 | -------------------------------------------------------------------------------- /byods/ascent-byods-rels/src/fake_vec.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | use std::ops::Index; 3 | 4 | pub struct FakeVec { 5 | _phantom: PhantomData, 6 | } 7 | 8 | impl Default for FakeVec { 9 | fn default() -> Self { Self { _phantom: PhantomData } } 10 | } 11 | 12 | impl FakeVec { 13 | #[inline(always)] 14 | pub fn push(&self, _: T) {} 15 | 16 | pub fn is_empty(&self) -> bool { self.len() == 0 } 17 | 18 | pub fn len(&self) -> usize { 0 } 19 | 20 | pub fn iter(&self) -> std::iter::Empty<&T> { std::iter::empty() } 21 | } 22 | 23 | impl Index for FakeVec { 24 | type Output = T; 25 | 26 | fn index(&self, _index: usize) -> &Self::Output { panic!("FakeVec is empty!") } 27 | } 28 | -------------------------------------------------------------------------------- /byods/ascent-byods-rels/src/iterator_from_dyn.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | 3 | pub struct IteratorFromDyn<'a, T> { 4 | iter: Box + 'a>, 5 | producer: Rc Box + 'a> + 'a>, 6 | } 7 | 8 | impl<'a, T> IteratorFromDyn<'a, T> { 9 | pub fn from_box_clo Box + 'a> + 'a>(producer: F) -> Self { 10 | let iter = producer(); 11 | Self { iter, producer: Rc::new(producer) as _ } 12 | } 13 | 14 | pub fn new Iter + 'a, Iter: Iterator + 'a>(producer: F) -> Self { 15 | Self::from_box_clo(move || Box::new(producer())) 16 | } 17 | } 18 | impl Iterator for IteratorFromDyn<'_, T> { 19 | type Item = T; 20 | 21 | #[inline(always)] 22 | fn next(&mut self) -> Option { self.iter.next() } 23 | } 24 | 25 | impl Clone for IteratorFromDyn<'_, T> { 26 | fn clone(&self) -> Self { Self { iter: (self.producer)(), producer: self.producer.clone() } } 27 | } 28 | -------------------------------------------------------------------------------- /byods/ascent-byods-rels/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! data structures for [`ascent`](https://github.com/s-arash/ascent) relations, 2 | //! made possible by Ascent's [BYODS](https://dl.acm.org/doi/pdf/10.1145/3622840) feature 3 | 4 | #![cfg_attr(not(test), deny(unused_crate_dependencies))] 5 | 6 | // See Cargo.toml for why this is needed. 7 | use syn as _; 8 | 9 | mod union_find; 10 | #[doc(hidden)] 11 | pub mod eqrel_ind; 12 | mod iterator_from_dyn; 13 | mod test; 14 | #[doc(hidden)] 15 | pub mod eqrel_binary; 16 | #[doc(hidden)] 17 | pub mod eqrel_ternary; 18 | mod rel_boilerplate; 19 | #[doc(hidden)] 20 | pub mod fake_vec; 21 | #[doc(hidden)] 22 | pub mod trrel_binary; 23 | #[doc(hidden)] 24 | pub mod trrel_ternary_ind; 25 | mod utils; 26 | 27 | #[doc(hidden)] 28 | pub mod trrel_binary_ind; 29 | #[doc(hidden)] 30 | pub mod binary_rel; 31 | #[cfg(feature = "par")] 32 | #[doc(hidden)] 33 | pub mod ceqrel_ind; 34 | pub mod trrel_union_find; 35 | #[doc(hidden)] 36 | pub mod trrel_union_find_binary_ind; 37 | 38 | pub mod uf; 39 | pub mod trrel; 40 | pub mod eqrel; 41 | pub mod trrel_uf; 42 | pub mod adaptor; 43 | -------------------------------------------------------------------------------- /byods/ascent-byods-rels/src/rel_boilerplate.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | 3 | use ascent::internal::{RelIndexMerge, RelIndexWrite}; 4 | 5 | pub struct NoopRelIndexWrite(PhantomData<(K, V)>); 6 | 7 | impl Default for NoopRelIndexWrite { 8 | #[inline(always)] 9 | fn default() -> Self { Self(PhantomData) } 10 | } 11 | 12 | impl RelIndexMerge for NoopRelIndexWrite { 13 | fn move_index_contents(_from: &mut Self, _to: &mut Self) {} 14 | } 15 | 16 | impl RelIndexWrite for NoopRelIndexWrite { 17 | type Key = K; 18 | type Value = V; 19 | #[inline(always)] 20 | fn index_insert(&mut self, _key: Self::Key, _value: Self::Value) {} 21 | } 22 | -------------------------------------------------------------------------------- /byods/ascent-byods-rels/src/trrel.rs: -------------------------------------------------------------------------------- 1 | //! transitive relations for Ascent 2 | 3 | pub use crate::eqrel::rel_codegen; 4 | 5 | #[doc(hidden)] 6 | #[macro_export] 7 | macro_rules! trrel_rel { 8 | ($name: ident, ($col0: ty, $col1: ty), $indices: expr, ser, ()) => { 9 | $crate::fake_vec::FakeVec<($col0, $col1)> 10 | }; 11 | 12 | // ternary 13 | ($name: ident, ($col0: ty, $col1: ty, $col2: ty), $indices: expr, ser, ()) => { 14 | $crate::fake_vec::FakeVec<($col0, $col1, $col2)> 15 | }; 16 | } 17 | pub use trrel_rel as rel; 18 | 19 | #[doc(hidden)] 20 | #[macro_export] 21 | macro_rules! trrel_rel_full_ind { 22 | ($name: ident, ($col0: ty, $col1: ty), $indices: expr, ser, (), $key: ty, $val: ty) => { 23 | $crate::trrel_binary_ind::ToTrRelIndFull<$col0> 24 | }; 25 | 26 | // ternary 27 | ($name: ident, ($col0: ty, $col1: ty, $col2: ty), $indices: expr, ser, (), $key: ty, $val: ty) => { 28 | $crate::trrel_ternary_ind::ToTrRel2IndFull<$col0, $col1> 29 | }; 30 | } 31 | pub use trrel_rel_full_ind as rel_full_ind; 32 | 33 | #[doc(hidden)] 34 | #[macro_export] 35 | macro_rules! trrel_rel_ind { 36 | ($name: ident, ($col0: ty, $col1: ty), $indices: expr, ser, (), [0], $key: ty, $val: ty) => { 37 | $crate::trrel_binary_ind::ToTrRelInd0<$col0> 38 | }; 39 | ($name: ident, ($col0: ty, $col1: ty), $indices: expr, ser, (), [1], $key: ty, $val: ty) => { 40 | $crate::trrel_binary_ind::ToTrRelInd1<$col0> 41 | }; 42 | ($name: ident, ($col0: ty, $col1: ty), $indices: expr, ser, (), [], $key: ty, $val: ty) => { 43 | $crate::trrel_binary_ind::ToTrRelIndNone<$col0> 44 | }; 45 | 46 | // ternary 47 | ($name: ident, ($col0: ty, $col1: ty, $col2: ty), $indices: expr, ser, (), [], $key: ty, $val: ty) => { 48 | $crate::trrel_ternary_ind::ToTrRel2IndNone<$col0, $col1> 49 | }; 50 | ($name: ident, ($col0: ty, $col1: ty, $col2: ty), $indices: expr, ser, (), [0], $key: ty, $val: ty) => { 51 | $crate::trrel_ternary_ind::ToTrRel2Ind0<$col0, $col1> 52 | }; 53 | ($name: ident, ($col0: ty, $col1: ty, $col2: ty), $indices: expr, ser, (), [1], $key: ty, $val: ty) => { 54 | $crate::trrel_ternary_ind::ToTrRel2Ind1<$col0, $col1> 55 | }; 56 | ($name: ident, ($col0: ty, $col1: ty, $col2: ty), $indices: expr, ser, (), [2], $key: ty, $val: ty) => { 57 | $crate::trrel_ternary_ind::ToTrRel2Ind2<$col0, $col1> 58 | }; 59 | ($name: ident, ($col0: ty, $col1: ty, $col2: ty), $indices: expr, ser, (), [0, 1], $key: ty, $val: ty) => { 60 | $crate::trrel_ternary_ind::ToTrRel2Ind0_1<$col0, $col1> 61 | }; 62 | ($name: ident, ($col0: ty, $col1: ty, $col2: ty), $indices: expr, ser, (), [0, 2], $key: ty, $val: ty) => { 63 | $crate::trrel_ternary_ind::ToTrRel2Ind0_2<$col0, $col1> 64 | }; 65 | ($name: ident, ($col0: ty, $col1: ty, $col2: ty), $indices: expr, ser, (), [1, 2], $key: ty, $val: ty) => { 66 | $crate::trrel_ternary_ind::ToTrRel2Ind1_2<$col0, $col1> 67 | }; 68 | ($name: ident, ($col0: ty, $col1: ty, $col2: ty), $indices: expr, ser, (), [0, 1, 2], $key: ty, $val: ty) => { 69 | $crate::trrel_ternary_ind::ToTrRel2IndFull<$col0, $col1> 70 | }; 71 | } 72 | pub use trrel_rel_ind as rel_ind; 73 | 74 | #[doc(hidden)] 75 | #[macro_export] 76 | macro_rules! trrel_rel_ind_common { 77 | ($name: ident, ($col0: ty, $col1: ty), $indices: expr, ser, ()) => { 78 | $crate::trrel_binary_ind::TrRelIndCommon<$col0> 79 | }; 80 | 81 | // ternary 82 | ($name: ident, ($col0: ty, $col1: ty, $col2: ty), $indices: tt, ser, ()) => { 83 | $crate::trrel_ternary_ind::TrRel2IndCommonWrapper< 84 | // reverse_map_1 required: 85 | {$crate::inds_contain!($indices, [1]) || $crate::inds_contain!($indices, [1, 2])}, 86 | // reverse_map_2 required: 87 | {$crate::inds_contain!($indices, [2]) || $crate::inds_contain!($indices, [1, 2])}, 88 | $col0, $col1> 89 | }; 90 | } 91 | pub use trrel_rel_ind_common as rel_ind_common; 92 | 93 | #[doc(hidden)] 94 | #[macro_export] 95 | macro_rules! inds_contain { 96 | ([], $ind: tt) => { 97 | false 98 | }; 99 | ([$head: tt], $ind: tt) => { 100 | ($crate::arrs_eq!($head, $ind)) 101 | }; 102 | ([$head: tt, $($tail: tt),*], $ind: tt) => { 103 | ($crate::arrs_eq!($head, $ind)) || $crate::inds_contain!([$($tail),*], $ind) 104 | }; 105 | } 106 | 107 | #[doc(hidden)] 108 | #[macro_export] 109 | macro_rules! arrs_eq { 110 | ([], []) => { true }; 111 | ([$x: expr], [$y: expr]) => { $x == $y }; 112 | ([$x: expr, $($xs: expr),*], [$y: expr, $($ys: expr),*]) => { 113 | $x == $y && $crate::arrs_eq!([$($xs),*], [$($ys),*]) 114 | }; 115 | ([$($xs: expr),*], [$($ys: expr),*]) => { false }; 116 | } 117 | 118 | #[test] 119 | fn test_arrs_eq() { 120 | let test1 = arrs_eq!([1, 2], [1, 2]); 121 | assert!(test1); 122 | assert!(!arrs_eq!([1], [1, 2])); 123 | assert!(arrs_eq!([1], [1])); 124 | assert!(arrs_eq!([], [])); 125 | assert!(!arrs_eq!([1, 2], [1])); 126 | } 127 | 128 | #[cfg(test)] 129 | #[allow(dead_code)] 130 | fn _test_trrel_rel_ind_common() { 131 | let _ind_common1: crate::trrel::rel_ind_common!(rel, (u64, u32, u32), [[], [0, 1], [0], [0, 1, 2]], ser, ()); 132 | let _ind_common2: crate::trrel::rel_ind_common!(rel, (u32, u64, u64), [[0, 1, 2], [0], [1], [0, 1]], ser, ()); 133 | } 134 | -------------------------------------------------------------------------------- /byods/ascent-byods-rels/src/trrel_binary.rs: -------------------------------------------------------------------------------- 1 | use std::hash::{BuildHasherDefault, Hash}; 2 | 3 | use hashbrown::HashMap; 4 | use rustc_hash::FxHasher; 5 | 6 | pub type MyHashSetIter<'a, T> = hashbrown::hash_set::Iter<'a, T>; 7 | pub type MyHashSet = hashbrown::HashSet; 8 | 9 | pub struct TrRel { 10 | pub(crate) map: HashMap>, BuildHasherDefault>, 11 | pub(crate) reverse_map: HashMap>, BuildHasherDefault>, 12 | // pub(crate) precursor_map: HashMap>, BuildHasherDefault>, 13 | // pub(crate) precursor_set: HashSet<(T, T), BuildHasherDefault>, 14 | pub(crate) precursor_set: Vec<(T, T)>, 15 | pub anti_reflexive: bool, 16 | } 17 | 18 | impl Default for TrRel { 19 | fn default() -> Self { 20 | Self { 21 | map: Default::default(), 22 | reverse_map: Default::default(), 23 | // precursor_map: Default::default(), 24 | precursor_set: Default::default(), 25 | anti_reflexive: true, 26 | } 27 | } 28 | } 29 | 30 | impl TrRel { 31 | /// returns true if this tuple did not exist in the transitive relation 32 | pub fn insert(&mut self, x: T, y: T) -> bool { 33 | // TODO is this correct? 34 | 35 | if x == y { 36 | return false; 37 | } 38 | 39 | if self.map.get(&x).is_some_and(|s| s.contains(&y)) { 40 | return false; 41 | } 42 | 43 | // if !self.precursor_map.entry(x.clone()).or_default().insert(y.clone()) { 44 | // return false; 45 | // } 46 | // if !self.precursor_set.insert((x.clone(), y.clone())) { 47 | // return false; 48 | // } 49 | self.precursor_set.push((x.clone(), y.clone())); 50 | 51 | let mut x_reverse_map = std::mem::take(self.reverse_map.entry(x.clone()).or_default()); 52 | let mut y_map = std::mem::take(self.map.entry(y.clone()).or_default()); 53 | // let y_map2 = y_map.iter().chain([&x]).map(|elem| (hash_one(self.map.hasher(), elem), elem.clone())).collect_vec(); 54 | for x_prime in x_reverse_map.iter().chain([&x]) { 55 | if x_prime != &y { 56 | let x_prime_map = self.map.entry(x_prime.clone()).or_default(); 57 | x_prime_map.extend(y_map.iter().chain([&y]).filter(|&a| a != x_prime).cloned()); 58 | // set_extend_with_hash_no_check(x_prime_map, y_map2.iter().cloned()); 59 | // for y_prime in y_map.iter().chain([&y]) { 60 | // self.reverse_map.entry(y_prime.clone()).or_default().insert(x_prime.clone()); 61 | // } 62 | } 63 | } 64 | 65 | // let x_reverse_map2 = x_reverse_map.iter().chain([&x]).map(|elem| (hash_one(self.map.hasher(), elem), elem.clone())).collect_vec(); 66 | for y_prime in y_map.iter().chain([&y]) { 67 | if y_prime != &x { 68 | let y_prime_reverse_map = self.reverse_map.entry(y_prime.clone()).or_default(); 69 | y_prime_reverse_map.extend(x_reverse_map.iter().chain([&x]).filter(|&a| a != y_prime).cloned()); 70 | // set_extend_with_hash_no_check(y_prime_reverse_map, x_reverse_map2.iter().cloned()); 71 | } 72 | } 73 | if x == y { 74 | x_reverse_map.insert(y.clone()); 75 | y_map.insert(x.clone()); 76 | } 77 | self.reverse_map.insert(x.clone(), x_reverse_map); 78 | self.map.insert(y.clone(), y_map); 79 | true 80 | } 81 | 82 | pub fn iter_all(&self) -> impl Iterator + '_ { 83 | self.map.iter().flat_map(|(x, x_set)| x_set.iter().map(move |y| (x, y))) 84 | } 85 | 86 | #[inline] 87 | pub fn contains(&self, x: &T, y: &T) -> bool { self.map.get(x).is_some_and(|s| s.contains(y)) } 88 | 89 | pub fn count_estimate(&self) -> usize { 90 | let sample_size = 3; 91 | let sum = self.map.values().take(sample_size).map(|x| x.len()).sum::(); 92 | sum * self.map.len() / sample_size.min(self.map.len()).max(1) 93 | } 94 | 95 | pub fn count_exact(&self) -> usize { self.map.values().map(|x| x.len()).sum() } 96 | } 97 | -------------------------------------------------------------------------------- /byods/ascent-byods-rels/src/trrel_uf.rs: -------------------------------------------------------------------------------- 1 | //! reflexive transitive relations for Ascent, supported by [`TrRelUnionFind`](crate::trrel_union_find::TrRelUnionFind) 2 | 3 | #[doc(hidden)] 4 | #[macro_export] 5 | macro_rules! trrel_uf_ind_common { 6 | ($name: ident, ($col0: ty, $col1: ty), $indices: expr, ser, ()) => { 7 | $crate::trrel_union_find_binary_ind::TrRelIndCommon<$col0> 8 | }; 9 | 10 | ($name: ident, ($col0: ty, $col1: ty, $col2: ty), $indices: tt, ser, ()) => { 11 | $crate::adaptor::bin_rel_to_ternary::BinRelToTernaryWrapper< 12 | // reverse_map_1 required: 13 | {$crate::inds_contain!($indices, [1]) || $crate::inds_contain!($indices, [1, 2])}, 14 | // reverse_map_2 required: 15 | {$crate::inds_contain!($indices, [2]) || $crate::inds_contain!($indices, [1, 2])}, 16 | $col0, $col1, $col2, 17 | $crate::trrel_union_find_binary_ind::TrRelIndCommon<$col1> 18 | > 19 | }; 20 | } 21 | pub use trrel_uf_ind_common as rel_ind_common; 22 | 23 | pub use crate::adaptor::bin_rel_plus_ternary_provider::{rel, rel_codegen, rel_full_ind, rel_ind}; 24 | -------------------------------------------------------------------------------- /byods/ascent-byods-rels/src/union_find.rs: -------------------------------------------------------------------------------- 1 | use std::hash::{BuildHasherDefault, Hash}; 2 | use std::iter::{FlatMap, Repeat, Zip}; 3 | 4 | #[cfg(feature = "par")] 5 | use ascent::rayon::prelude::{IntoParallelRefIterator, ParallelIterator}; 6 | use hashbrown::hash_set::Iter as HashSetIter; 7 | use hashbrown::{HashMap, HashSet}; 8 | use rustc_hash::FxHasher; 9 | 10 | use crate::utils::merge_sets; 11 | 12 | #[derive(Clone, Debug)] 13 | pub struct EqRel { 14 | pub(crate) sets: Vec>>, 15 | pub(crate) elem_ids: HashMap>, 16 | pub(crate) set_subsumptions: HashMap>, 17 | } 18 | 19 | impl Default for EqRel { 20 | fn default() -> Self { 21 | Self { sets: Default::default(), elem_ids: Default::default(), set_subsumptions: Default::default() } 22 | } 23 | } 24 | 25 | pub type IterAllIterator<'a, T> = FlatMap< 26 | std::slice::Iter<'a, HashSet>>, 27 | FlatMap< 28 | Zip, Repeat>>, 29 | Zip, Repeat<&'a T>>, 30 | for<'aa> fn((&'aa T, HashSetIter<'aa, T>)) -> Zip, Repeat<&'aa T>>, 31 | >, 32 | fn( 33 | &HashSet>, 34 | ) -> FlatMap< 35 | Zip, Repeat>>, 36 | Zip, Repeat<&T>>, 37 | for<'aa> fn((&'aa T, HashSetIter<'aa, T>)) -> Zip, Repeat<&'aa T>>, 38 | >, 39 | >; 40 | 41 | #[cfg(feature = "par")] 42 | pub struct IterAllParIterator<'a, T: Clone + Hash + Eq>(&'a EqRel); 43 | #[cfg(feature = "par")] 44 | impl<'a, T: Clone + Hash + Eq + Sync> ParallelIterator for IterAllParIterator<'a, T> { 45 | type Item = (&'a T, &'a T); 46 | 47 | fn drive_unindexed(self, consumer: C) -> C::Result 48 | where C: ascent::rayon::iter::plumbing::UnindexedConsumer { 49 | self 50 | .0 51 | .sets 52 | .par_iter() 53 | .flat_map:: _, _>(|s| { 54 | s.par_iter().map_with(s, |s, x| s.par_iter().map_with(x, |x, y| (*x, y))).flatten() 55 | }) 56 | .drive_unindexed(consumer) 57 | } 58 | } 59 | 60 | impl EqRel { 61 | fn get_dominant_id(&self, id: usize) -> usize { 62 | match self.set_subsumptions.get(&id) { 63 | Some(dom_id) => self.get_dominant_id(*dom_id), 64 | None => id, 65 | } 66 | } 67 | pub(crate) fn elem_set(&self, elem: &T) -> Option { 68 | self.elem_ids.get(elem).map(|id| self.get_dominant_id(*id)) 69 | } 70 | 71 | fn get_dominant_id_update(&mut self, id: usize) -> usize { 72 | match self.set_subsumptions.get(&id) { 73 | Some(&parent_id) => { 74 | let dom_id = self.get_dominant_id_update(parent_id); 75 | if dom_id != parent_id { 76 | self.set_subsumptions.insert(id, dom_id); 77 | } 78 | dom_id 79 | }, 80 | None => id, 81 | } 82 | } 83 | pub(crate) fn elem_set_update(&mut self, elem: &T) -> Option { 84 | let id = self.elem_ids.get(elem)?; 85 | Some(self.get_dominant_id_update(*id)) 86 | } 87 | 88 | pub fn add(&mut self, x: T, y: T) -> bool { 89 | let x_set = self.elem_set_update(&x); 90 | let y_set = self.elem_set_update(&y); 91 | match (x_set, y_set) { 92 | (None, None) => { 93 | let id = self.sets.len(); 94 | self.sets.push(HashSet::from_iter([x.clone(), y.clone()])); 95 | self.elem_ids.insert(x.clone(), id); 96 | self.elem_ids.insert(y.clone(), id); 97 | true 98 | }, 99 | (None, Some(y_set)) => { 100 | self.sets[y_set].insert(x.clone()); 101 | self.elem_ids.insert(x, y_set); 102 | true 103 | }, 104 | (Some(x_set), None) => { 105 | self.sets[x_set].insert(y.clone()); 106 | self.elem_ids.insert(y, x_set); 107 | true 108 | }, 109 | (Some(x_set), Some(y_set)) => 110 | if x_set != y_set { 111 | let y_set_taken = std::mem::take(&mut self.sets[y_set]); 112 | merge_sets(&mut self.sets[x_set], y_set_taken); 113 | self.set_subsumptions.insert(y_set, x_set); 114 | true 115 | } else { 116 | false 117 | }, 118 | } 119 | } 120 | 121 | pub fn set_of(&self, x: &T) -> Option> { 122 | let set = self.elem_set(x)?; 123 | let res = Some(self.sets[set].iter()); 124 | res 125 | } 126 | 127 | #[cfg(feature = "par")] 128 | pub fn c_set_of(&self, x: &T) -> Option<&'_ hashbrown::hash_set::HashSet>> 129 | where T: Sync { 130 | let set = self.elem_set(x)?; 131 | Some(&self.sets[set]) 132 | } 133 | 134 | // TODO not used 135 | #[allow(dead_code)] 136 | fn set_of_inc_x<'a>(&'a self, x: &'a T) -> impl Iterator { 137 | let set = self.set_of(x); 138 | let x_itself = if set.is_none() { Some(x) } else { None }; 139 | set.into_iter().flatten().chain(x_itself) 140 | } 141 | 142 | pub fn iter_all(&self) -> IterAllIterator<'_, T> { 143 | let res: IterAllIterator<'_, T> = self 144 | .sets 145 | .iter() 146 | .flat_map(|s| s.iter().zip(std::iter::repeat(s.iter())).flat_map(|(x, s)| s.zip(std::iter::repeat(x)))); 147 | res 148 | } 149 | 150 | #[cfg(feature = "par")] 151 | pub fn c_iter_all(&self) -> IterAllParIterator<'_, T> 152 | where T: Sync { 153 | IterAllParIterator(self) 154 | } 155 | 156 | pub fn contains(&self, x: &T, y: &T) -> bool { self.elem_set(x).is_some_and(|set| self.sets[set].contains(y)) } 157 | 158 | pub fn combine(&mut self, other: Self) { 159 | for set in other.sets.into_iter() { 160 | #[allow(clippy::comparison_chain)] 161 | if set.len() == 1 { 162 | let repr = set.into_iter().next().unwrap(); 163 | self.add(repr.clone(), repr); 164 | } else if set.len() > 1 { 165 | let mut set = set.into_iter(); 166 | let repr = set.next().unwrap(); 167 | for x in set { 168 | self.add(repr.clone(), x); 169 | } 170 | } 171 | } 172 | } 173 | 174 | pub fn count_exact(&self) -> usize { self.sets.iter().map(|s| s.len() * s.len()).sum() } 175 | } 176 | 177 | #[test] 178 | fn test_eq_rel() { 179 | let mut eqrel = EqRel::::default(); 180 | eqrel.add(1, 2); 181 | eqrel.add(11, 12); 182 | assert!(eqrel.contains(&1, &2)); 183 | assert!(!eqrel.contains(&1, &12)); 184 | eqrel.add(1, 3); 185 | eqrel.add(13, 12); 186 | assert!(!eqrel.contains(&2, &13)); 187 | eqrel.add(3, 11); 188 | assert!(eqrel.contains(&2, &13)); 189 | } 190 | 191 | #[test] 192 | fn test_eq_rel_combine() { 193 | let mut eqrel1 = EqRel::::default(); 194 | eqrel1.add(1, 2); 195 | eqrel1.add(1, 3); 196 | eqrel1.add(1, 10); 197 | 198 | let mut eqrel2 = EqRel::::default(); 199 | eqrel2.add(10, 11); 200 | eqrel2.add(11, 12); 201 | eqrel2.add(13, 12); 202 | 203 | assert!(!eqrel1.contains(&1, &13)); 204 | eqrel1.combine(eqrel2); 205 | assert!(eqrel1.contains(&1, &13)); 206 | } 207 | -------------------------------------------------------------------------------- /byods/ascent-byods-rels/src/utils.rs: -------------------------------------------------------------------------------- 1 | use std::hash::{BuildHasher, Hash}; 2 | 3 | use hashbrown::{Equivalent, HashMap, HashSet}; 4 | 5 | use crate::iterator_from_dyn::IteratorFromDyn; 6 | use crate::trrel_binary::MyHashSet; 7 | 8 | pub(crate) fn move_hash_map_of_hash_set_contents( 9 | from: &mut HashMap, S1>, to: &mut HashMap, S1>, 10 | ) where 11 | K: Clone + Hash + Eq, 12 | V: Clone + Hash + Eq, 13 | S1: BuildHasher, 14 | S2: BuildHasher, 15 | { 16 | if from.len() > to.len() { 17 | std::mem::swap(from, to); 18 | } 19 | 20 | for (k, mut from_set) in from.drain() { 21 | match to.entry(k) { 22 | hashbrown::hash_map::Entry::Occupied(mut to_set) => { 23 | if from_set.len() > to_set.get().len() { 24 | std::mem::swap(&mut from_set, to_set.get_mut()) 25 | } 26 | to_set.get_mut().extend(from_set.drain()); 27 | }, 28 | hashbrown::hash_map::Entry::Vacant(to_set_vac) => { 29 | to_set_vac.insert(from_set); 30 | }, 31 | } 32 | } 33 | } 34 | 35 | pub(crate) fn move_hash_map_of_vec_contents( 36 | from: &mut HashMap, S1>, to: &mut HashMap, S1>, 37 | ) where 38 | K: Clone + Hash + Eq, 39 | V: Clone + Hash + Eq, 40 | S1: BuildHasher, 41 | { 42 | if from.len() > to.len() { 43 | std::mem::swap(from, to); 44 | } 45 | 46 | for (k, mut from_vec) in from.drain() { 47 | match to.entry(k) { 48 | hashbrown::hash_map::Entry::Occupied(mut to_vec) => { 49 | if from_vec.len() > to_vec.get().len() { 50 | std::mem::swap(&mut from_vec, to_vec.get_mut()) 51 | } 52 | to_vec.get_mut().append(&mut from_vec); 53 | }, 54 | hashbrown::hash_map::Entry::Vacant(to_vec_vac) => { 55 | to_vec_vac.insert(from_vec); 56 | }, 57 | } 58 | } 59 | } 60 | 61 | pub(crate) fn move_hash_map_of_hash_set_contents_disjoint( 62 | from: &mut HashMap, S1>, to: &mut HashMap, S1>, 63 | ) where 64 | K: Clone + Hash + Eq, 65 | V: Clone + Hash + Eq, 66 | S1: BuildHasher, 67 | S2: BuildHasher, 68 | { 69 | if from.len() > to.len() { 70 | std::mem::swap(from, to); 71 | } 72 | 73 | for (k, mut from_set) in from.drain() { 74 | match to.entry(k) { 75 | hashbrown::hash_map::Entry::Occupied(mut to_set) => { 76 | move_hash_set_contents_disjoint(&mut from_set, to_set.get_mut()); 77 | }, 78 | hashbrown::hash_map::Entry::Vacant(to_set_vac) => { 79 | to_set_vac.insert(from_set); 80 | }, 81 | } 82 | } 83 | } 84 | 85 | pub fn move_hash_set_contents_disjoint(from: &mut HashSet, to: &mut HashSet) { 86 | if from.len() > to.len() { 87 | std::mem::swap(from, to); 88 | } 89 | to.reserve(from.len()); 90 | for x in from.drain() { 91 | to.insert_unique_unchecked(x); 92 | } 93 | } 94 | 95 | pub(crate) fn move_hash_map_of_alt_hash_set_contents( 96 | from: &mut HashMap, S1>, to: &mut HashMap, S1>, 97 | ) where 98 | K: Clone + Hash + Eq, 99 | V: Clone + Hash + Eq, 100 | S1: BuildHasher, 101 | S2: BuildHasher, 102 | { 103 | if from.len() > to.len() { 104 | std::mem::swap(from, to); 105 | } 106 | 107 | for (k, mut from_set) in from.drain() { 108 | match to.entry(k) { 109 | hashbrown::hash_map::Entry::Occupied(mut to_set) => { 110 | if from_set.len() > to_set.get().len() { 111 | std::mem::swap(&mut from_set, to_set.get_mut()) 112 | } 113 | to_set.get_mut().extend(from_set.drain()); 114 | }, 115 | hashbrown::hash_map::Entry::Vacant(to_set_vac) => { 116 | to_set_vac.insert(from_set); 117 | }, 118 | } 119 | } 120 | } 121 | 122 | pub struct AltHashSet(pub(crate) HashMap); 123 | 124 | impl Default for AltHashSet { 125 | #[inline(always)] 126 | fn default() -> Self { Self(Default::default()) } 127 | } 128 | 129 | impl AltHashSet { 130 | #[inline(always)] 131 | pub fn contains>(&self, k: &Q) -> bool { self.0.contains_key(k) } 132 | 133 | #[inline(always)] 134 | pub fn iter(&self) -> AltHashSetIter<'_, T> { self.0.keys() } 135 | 136 | #[inline(always)] 137 | pub fn insert(&mut self, x: T) -> bool { self.0.insert(x, ()).is_none() } 138 | 139 | #[inline(always)] 140 | pub fn len(&self) -> usize { self.0.len() } 141 | 142 | pub fn extend>(&mut self, iter: Iter) { 143 | self.0.extend(iter.into_iter().map(|x| (x, ()))) 144 | } 145 | 146 | #[inline] 147 | pub fn insert_with_hash_no_check(&mut self, hash: u64, item: T) { 148 | self.0.raw_entry_mut().from_key_hashed_nocheck(hash, &item).or_insert(item, ()); 149 | } 150 | 151 | pub fn drain(&mut self) -> impl Iterator + '_ { self.0.drain().map(|kv| kv.0) } 152 | 153 | pub fn intersection<'a>(&'a self, other: &'a Self) -> impl Iterator + 'a { 154 | let (small, big) = if self.len() < other.len() { (self, other) } else { (other, self) }; 155 | small.iter().filter(|&x| big.contains(x)) 156 | } 157 | } 158 | pub type AltHashSetIter<'a, T> = hashbrown::hash_map::Keys<'a, T, ()>; 159 | 160 | // TODO remove if not used 161 | fn _set_extend_with_hash_no_check(set: &mut AltHashSet, iter: Iter) 162 | where 163 | T: Clone + Hash + Eq, 164 | S: BuildHasher, 165 | Iter: Iterator, 166 | { 167 | set.0.reserve(iter.size_hint().0); 168 | for (hash, item) in iter { 169 | set.0.raw_entry_mut().from_key_hashed_nocheck(hash, &item).insert(item, ()); 170 | } 171 | } 172 | 173 | // TODO remove if not used 174 | #[allow(dead_code)] 175 | pub fn hash_map_hash_set_intersection<'a, K, V, S>( 176 | hm: &'a HashMap, hs: &'a MyHashSet, 177 | ) -> IteratorFromDyn<'a, &'a V> 178 | where 179 | K: Clone + Hash + Eq, 180 | S: BuildHasher, 181 | { 182 | if hm.len() < hs.len() { 183 | IteratorFromDyn::new(move || hm.iter().filter_map(move |(k, v)| if hs.contains(k) { Some(v) } else { None })) 184 | } else { 185 | IteratorFromDyn::new(|| hs.iter().filter_map(|k| hm.get(k))) 186 | } 187 | } 188 | 189 | #[derive(Clone, Eq, PartialEq, Debug, Hash)] 190 | #[allow(dead_code)] 191 | pub enum Either { 192 | Left(L), 193 | Right(R), 194 | } 195 | 196 | impl Iterator for Either 197 | where 198 | L: Iterator, 199 | R: Iterator, 200 | { 201 | type Item = T; 202 | 203 | #[inline] 204 | fn next(&mut self) -> Option { 205 | match self { 206 | Either::Left(l) => l.next(), 207 | Either::Right(r) => r.next(), 208 | } 209 | } 210 | } 211 | 212 | pub(crate) fn merge_sets(set1: &mut HashSet, mut set2: HashSet) { 213 | if set1.len() < set2.len() { 214 | std::mem::swap(set1, &mut set2); 215 | } 216 | set1.extend(set2); 217 | } 218 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | # https://github.com/rust-lang/rustfmt/blob/master/Configurations.md 2 | tab_spaces = 3 3 | max_width = 120 4 | fn_call_width = 110 5 | use_small_heuristics = "Max" 6 | single_line_if_else_max_width = 110 7 | single_line_let_else_max_width = 110 8 | struct_lit_width = 110 9 | struct_variant_width = 110 10 | short_array_element_width_threshold = 100 11 | fn_single_line = true 12 | fn_params_layout = "Compressed" 13 | match_arm_blocks = false 14 | newline_style = "Unix" 15 | overflow_delimited_expr = true 16 | reorder_modules = false 17 | trailing_semicolon = false 18 | unstable_features = true 19 | format_macro_bodies = false 20 | match_block_trailing_comma = true 21 | merge_derives = false 22 | style_edition = "2024" 23 | use_field_init_shorthand = true 24 | where_single_line = true 25 | group_imports = "StdExternalCrate" 26 | imports_granularity = "Module" 27 | -------------------------------------------------------------------------------- /wasm-tests/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | bin/ 5 | pkg/ 6 | wasm-pack.log 7 | -------------------------------------------------------------------------------- /wasm-tests/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wasm-tests" 3 | version = "0.1.0" 4 | authors = ["Arash Sahebolamri "] 5 | edition = "2021" 6 | 7 | [lib] 8 | crate-type = ["cdylib", "rlib"] 9 | 10 | [features] 11 | default = ["console_error_panic_hook"] 12 | 13 | [dependencies] 14 | ascent = { path = "../ascent", features = ["wasm-bindgen"] } 15 | 16 | bumpalo = "3.16" 17 | 18 | # The `console_error_panic_hook` crate provides better debugging of panics by 19 | # logging them with `console.error`. This is great for development, but requires 20 | # all the `std::fmt` and `std::panicking` infrastructure, so isn't great for 21 | # code size when deploying. 22 | console_error_panic_hook = { version = "0.1.6", optional = true } 23 | 24 | [dev-dependencies] 25 | wasm-bindgen-test = "0.3.13" 26 | 27 | [profile.release] 28 | # Tell `rustc` to optimize for small code size. 29 | opt-level = "s" 30 | -------------------------------------------------------------------------------- /wasm-tests/README.md: -------------------------------------------------------------------------------- 1 | ### For testing Ascent running on WASM 2 | 3 | Generated by: 4 | ``` 5 | cargo generate --git https://github.com/rustwasm/wasm-pack-template.git --name my-project 6 | cd my-project 7 | ``` 8 | 9 | Build: 10 | ``` 11 | wasm-pack build 12 | ``` 13 | 14 | Test: 15 | 16 | ``` 17 | wasm-pack test --headless --firefox 18 | ``` 19 | 20 | Dependencies: 21 | * [`wasm-bindgen`](https://github.com/rustwasm/wasm-bindgen) for communicating 22 | between WebAssembly and JavaScript. 23 | * [`console_error_panic_hook`](https://github.com/rustwasm/console_error_panic_hook) 24 | for logging panic messages to the developer console. 25 | -------------------------------------------------------------------------------- /wasm-tests/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod utils; 2 | -------------------------------------------------------------------------------- /wasm-tests/src/utils.rs: -------------------------------------------------------------------------------- 1 | #[allow(dead_code)] 2 | pub fn set_panic_hook() { 3 | // When the `console_error_panic_hook` feature is enabled, we can call the 4 | // `set_panic_hook` function at least once during initialization, and then 5 | // we will get better error messages if our code ever panics. 6 | // 7 | // For more details see 8 | // https://github.com/rustwasm/console_error_panic_hook#readme 9 | #[cfg(feature = "console_error_panic_hook")] 10 | console_error_panic_hook::set_once(); 11 | } 12 | -------------------------------------------------------------------------------- /wasm-tests/tests/web.rs: -------------------------------------------------------------------------------- 1 | //! Test suite for the Web and headless browsers. 2 | 3 | #![cfg(target_arch = "wasm32")] 4 | 5 | extern crate wasm_bindgen_test; 6 | use ascent::ascent; 7 | use wasm_bindgen_test::*; 8 | 9 | wasm_bindgen_test_configure!(run_in_browser); 10 | 11 | #[wasm_bindgen_test] 12 | fn test_ascent_wasm() { 13 | ascent! { 14 | relation r(i32, i32); 15 | relation tc(i32, i32); 16 | 17 | tc(x, y) <-- r(x, y); 18 | tc(x, z) <-- r(x, y), tc(y, z); 19 | } 20 | let mut prog = AscentProgram::default(); 21 | prog.r = vec![(1, 2), (2, 3)]; 22 | prog.run(); 23 | 24 | println!("tc: {:?}", prog.tc); 25 | assert_eq!(prog.tc.len(), 3); 26 | } 27 | --------------------------------------------------------------------------------