Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

accept quads #10

Merged
merged 13 commits into from
Mar 9, 2021
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions .github/workflows/default.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
on: [pull_request]

name: CI

jobs:
all:
name: Normal Build, Test Suite, Test JS bindings, Rustfmt, Clippy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
override: true
components: rustfmt, clippy
- uses: extractions/setup-just@v1
- uses: jetli/[email protected]
- uses: actions/setup-node@v1
with:
node-version: '13.x'
- run: cargo build
- run: cargo test
- run: just js-test
- run: cargo clippy -- -D warnings
- run: cargo fmt --all -- --check
16 changes: 7 additions & 9 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "rify"
version = "0.5.1"
version = "0.6.0-rc.1"
authors = [
"Andrew Dirksen <[email protected]>",
"Sam Hellawell <[email protected]>"
Expand All @@ -15,18 +15,16 @@ documentation = "https://docs.rs/rify"
readme = "README.md"
repository = "https://github.com/docknetwork/rify"

[package.metadata.wasm-pack.profile.release]
wasm-opt = false

[lib]
crate-type = ["cdylib", "lib"]
crate-type = ["lib"]

[dependencies]
wasm-bindgen = { version = "0.2", optional = true, features = ["serde-serialize"] }
serde = { version = "1", optional = true, features = ["derive"] }
serde_json = { version = "1", optional = true }

[dev-dependencies]
rify = { path = ".", features = [ "serde", "std" ] } # enables these features for tests
serde_json = "1"

[features]
default = ["std", "js-library-wasm"]
js-library-wasm = ["wasm-bindgen", "serde", "serde_json"]
default = []
std = []
79 changes: 58 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,46 @@

Rify is a [forward chaining](https://en.wikipedia.org/wiki/Forward_chaining) [inference engine](https://en.wikipedia.org/wiki/Inference_engine) designed specifically for use on in-memory RDF graphs.

It accepts conjunctive rules with limited expressiveness so reasoning is bounded by `O(n^k)` in both memory and in computation where n is the number of nodes in the input RDF graph.
It accepts conjunctive rules with limited expressiveness so reasoning is bounded by `O(n^k)` in both memory and in computation where n is the number of nodes in the input RDF dataset.

Reasoning generates a proof which can be used to quickly verify the result pragmatically.

Logical rules are defined as if-then clauses. Something like this:

```rust
struct Rule {
if_all: Vec<[Entity; 3]>,
then: Vec<[Entity; 3]>,
if_all: Vec<[Entity; 4]>,
then: Vec<[Entity; 4]>,
}

// Notice it's `[Entity; 4]`, not `[Entity; 3]`. This reasoner operates on Rdf Quads, not triples.
// You can still reason over triples by binding a unique resource e.g. `RdfTerm::DefaultGraph`
// as the graph in all rules and all quads.

enum Entity {
/// A a named variable with an unknown value.
Unbound(String),
/// A literal RDF node.
Bound(RdfNode),
/// A literal with a constant value.
Bound(RdfTerm),
}

// actual definitions of these types are templated but this is the gist

// You get to define this type! Here is an example definition.
enum RdfTerm {
Blank(usize),
Iri(String),
Literal {
datatype: String,
value: String,
lang_tag: Option<String>,
},
DefaultGraph,
}
```

Rify doesn't care whether its input and output is valid rdf. For example, it will happily accept a quad whose predicate is a literal. https://www.w3.org/TR/rdf11-concepts/#section-triples

# Use

Two functions are central to this library: `prove` and `validate`.
Expand All @@ -44,25 +62,37 @@ use rify::{
// (?a, is, awesome) ∧ (?a, score, ?s) -> (?a score, awesome)
let awesome_score_axiom = Rule::create(
vec![
[Unbound("a"), Bound("is"), Bound("awesome")], // if someone is awesome
[Unbound("a"), Bound("score"), Unbound("s")], // and they have some score
// if someone is awesome
[Unbound("a"), Bound("is"), Bound("awesome"), Bound("default_graph")],
// and they have some score
[Unbound("a"), Bound("score"), Unbound("s"), Bound("default_graph")],
],
vec![
// then they must have an awesome score
[Unbound("a"), Bound("score"), Bound("awesome"), Bound("default_graph")]
],
vec![[Unbound("a"), Bound("score"), Bound("awesome")]], // then they must have an awesome score
)?;
).expect("invalid rule");

assert_eq!(
prove::<&str, &str>(
&[["you", "score", "unspecified"], ["you", "is", "awesome"]],
&[["you", "score", "awesome"]],
// list of already known facts (premises)
&[
["you", "score", "unspecified", "default_graph"],
["you", "is", "awesome", "default_graph"]
],
// the thing we want to prove
&[["you", "score", "awesome", "default_graph"]],
// ordered list of logical rules
&[awesome_score_axiom]
)?,
&[
),
Ok(vec![
// the desired statement is proven by instantiating the `awesome_score_axiom`
// (you is awesome) ∧ (you score unspecified) -> (you score awesome)
RuleApplication {
rule_index: 0,
instantiations: vec!["you", "unspecified"]
}
]
])
);
```

Expand All @@ -72,10 +102,17 @@ assert_eq!(
use rify::{
prove, validate, Valid,
Rule, RuleApplication,
Entity::{Bound, Unbound}
};

// (?a, is, awesome) ∧ (?a, score, ?s) -> (?a score, awesome)
let awesome_score_axiom = ...;
// same as above
let awesome_score_axiom = Rule::create(
vec![
[Unbound("a"), Bound("is"), Bound("awesome"), Bound("default_graph")],
[Unbound("a"), Bound("score"), Unbound("s"), Bound("default_graph")],
],
vec![[Unbound("a"), Bound("score"), Bound("awesome"), Bound("default_graph")]],
).expect("invalid rule");

let proof = vec![
// (you is awesome) ∧ (you score unspecified) -> (you score awesome)
Expand All @@ -88,16 +125,16 @@ let proof = vec![
let Valid { assumed, implied } = validate::<&str, &str>(
&[awesome_score_axiom],
&proof,
).map_err(|e| format!("{:?}", e))?;
).expect("invalid proof");

// Now we know that under the given rules, if all RDF triples in `assumed` are true, then all
// RDF triples in `implied` are also true.
// Now we know that under the given rules, if all quads in `assumed` are true, then all
// quads in `implied` are also true.
```

# recipies
# Recipes

In addition to normal cargo commands like `cargo test` and `cargo check` the `./justfile`
defines some scripts which can be useful during develompent. For example, `just js-test` will
defines some scripts which can be useful during development. For example, `just js-test` will
test the javascript bindings to this library. See `./justfile` for more.

# License
Expand Down
5 changes: 5 additions & 0 deletions bindings/js_wasm/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/target
/pkg
/wasm-pack.log
/tmp
/Cargo.lock
28 changes: 28 additions & 0 deletions bindings/js_wasm/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
[package]
name = "rify_js"
version = "0.0.1"
authors = [
"Andrew Dirksen <[email protected]>",
"Sam Hellawell <[email protected]>"
]
edition = "2018"
description = """
RDF reasoner that operates on RIF-like conjunctive rules. Outputs a machine
readable proof of some claim which can be cheaply verified.
"""
license = "MIT OR Apache-2.0"
documentation = "https://docs.rs/rify"
readme = "README.md"
repository = "https://github.com/docknetwork/rify"

[package.metadata.wasm-pack.profile.release]
wasm-opt = false

[lib]
crate-type = ["cdylib"]

[dependencies]
wasm-bindgen = { version = "0.2", features = ["serde-serialize"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
rify = { path = "../..", features = ["serde"] }
5 changes: 5 additions & 0 deletions bindings/js_wasm/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Changelog

## 0.6.0

Breaking change! Changed public methods to accept and return quads instead of triples.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"version": "1.0.0",
"license": "MIT",
"dependencies": {
"rify": "../../pkg",
"rify": "../pkg",
"chai": "^4.2.0"
},
"devDependencies": {
Expand Down
Loading