npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

rdf-tension

v0.1.0

Published

An RDF surfaces reasoner

Downloads

4

Readme

Tension

An RDF surfaces reasoner.

This is currently a very incomplete README to just have something at least. Some random notes:

  • Program can be run with node bin/tension.js --help to see the available CLI options.
  • Run with info logging (default) to only see the deduced triples, run with debug to see what is going on internally.
  • Probably not completely logically sound. Triples with the same blank node twice or some weird existential/universal combinations will probably trip it up.
  • Not handling existentials that well yet.
  • Tests can be found at https://github.com/eyereasoner/rdfsurfaces-tests.

Using as npm dependency

npm install rdf-tension

import { readFileSync } from 'node:fs';
import { run } from 'rdf-tension';

const input = readFileSync('my/surfaces/file.n3s').toString();

// All options besides `input` are optional.
// The values below are the defaults.
const options = {
  input,
  // Set this to 0 for infinite steps
  maxSteps: 5,
  ignoreAnswer: false,
  logLevle: 'error',
};

// Note that the resulting quads can also have a List or Graph as term value
for await (const { quad, positive } of run(options)) {
  // Do something with the results
  myFunc(quad, positive);
}

How it works

First each surface gets converted into a disjunction of conjunctions. For the graffiti we keep track of how deep that graffiti was, to later determine if this is an existential or universal, and how it relates to other graffiti. Blank nodes with the same name that occur in different scopes will be renamed to prevent issues.

For example:

() log:onNegativeSurface {
    () log:onNegativeSurface {
        :A a :Statement .
        () log:onNegativeSurface {
            :A a :Statement .
        } .
    } .
    () log:onNegativeSurface {
        :test2 :is true .
    } .
} .

Will be converted to ((:A a :Statement) && -(:A a :Statement)) || (:test2 :is true), with - indicating negation, && conjunction, and || disjunction. All these surfaces are then put together into a single conjunction at root level, together with any triples at the root of the document. Internally in the code level 0/1/2 is used to refer to the root clause, disjunction level, and (internal) conjunction level.

The reasoner has three main reasoning "blocks", which each correspond to a set of logical rules that get applied to the known dataset to generate/improve data. These each get executed in a fixed order. One such iteration is called a step. The maximum amount of steps that the reasoner should execute can be set using the CLI. The three blocks are called "simplify", "bind", and "overlap".

By default, the reasoner will keep going until it really can't find anything new, or it solves an answer surface.

Simplify

This step tries to remove clauses or triples to simplify their interpretation.

At conjunction level this is done by trying to find a contradiction with the root triples and other triples in the conjunction. E.g., using A,B,C,... for constants, x,y,z,... for variables, and f,g,h,... for functions (as stand-in for triples), knowing ∀x: f(x) allows us to reduce g(B) || (-f(A) && g(C)) to g(B).

In the same way we look for tautologies at disjunction level, e.g. f(A) || -f(A). Finding one of these allows us to remove the disjunction completely as it won't give us interesting information.

On both levels we also remove duplicates or similar superfluous information. E.g., ∀x: f(x) || f(A) becomes ∀x: f(x).

Bind

In this step we generate new clauses by binding universals to specific constants that might lead to better results. How to bind which universal to which variable is determined by trying to match root level triples with triple patterns in any clause. For example, knowing :A a :Statement and ∀x: (?x a :Statement) || (?x a :Person), would result in the new clause (:A a :Statement) || (:A a :Person).

Bind results are cached, so the same binding will not be tried twice. Newly generated triples will first be simplified, after which the reasoner will check if it already knows this information or not, either by having an identical clause, or one that is more specific. Only if this is not the case will the new clause be added to known data. The same applies for the clauses generated in the overlap step below.

Overlap

In the overlap step the reasoner tries to combine two clauses to generate a new clause. This is done by finding parts of two clauses that contradict each other and replacing them with the rest. E.g, f(A) || f(B) and -f(A) || f(C) will result in f(B) || f(C).

Overlap is also checked by comparing conjunction parts, so f(A) || (f(B) && f(C)) and g(A) || (-f(B) && g(C)) will result in two new clauses: f(A) || (g(A) && f(C)) and g(A) || (f(A) && g(C)).

Overlap can also be done with universals present. ∀x: f(x) || g(x) and -f(A) || g(B) will generate g(A) || g(B).

As a pruning strategy, overlap results that have more quads than their parents combined, will not be stored.

Built-ins

Tension has some support for N3 built-ins. Each built-in implementation can fulfill two functions: a boolean check to determine if the statement is true/false, and a bind step where a new clause can be generated by replacing blank nodes with values determined by the built-in. Every built-in is implemented in its own file that exposes the functions necessary to do this, these can be seen in src/builtins. New built-ins can be supported by adding a file to that folder.

For example, f(A) || f(B) || sum((2 3), 20) would be replaced with f(A) || f(B) as the sum check returns false. f(x) || f(B) || sum((2 3), x) would be replaced with f(5) || f(B) as the built-in would apply the binding x: 5.