Skip to content

Example: Lives & Knows

Verena Blaschke edited this page Jun 30, 2022 · 10 revisions

A very simple project demonstrating how to use the psl-infrastructure and psl-ragviewer packages. This demo project is based on a PSL problem for inferring whether a pair of people knows each other based on information whether they share the same address.

Classes

Predicates

We have two predicates:

  • LivesPred: Lives(P,L) -- "Person P lives at address L"
  • KnowsPred: Knows(P1,P2) -- "Person P1 knows person P2"

Both predicate classes contain templates for describing a ground atom as a noun phrase ("Alice knowing Bob") or a sentence ("Alice probably knows Bob").

The SampleConstantRenderer provides pretty-print functions for some of the atom arguments.

Rules

We have one weighted logical rule and one arithmetic constraint:

  • LivesToKnowsRule: 1.0: Lives(P1,L) & Lives(P2,L) & (P1 != P2) -> Knows(P1,P2) -- "If there is evidence that two people live at the same address, this makes it more likely that they know each other."
  • KnowsSymmetryConstraint: Knows(P1,P2) = Knows(P2,P1) . -- "The knows relationship is symmetric."

Each rule overrides the method generateExplanation to provide explanation templates that match the rule logic more naturally than the default explanations might.

Each custom rule class needs to extend one of the following classes:

  • TalkingLogicalRule: weighted logical rules
  • TalkingLogicalConstraint: logical constraints
  • TalkingArithmeticRule: weighted arithmetic rules
  • TalkingArithmeticConstraint: arithmetic constraints

The PSL problem definition

In the SamplePslProblem, we add the two predicates to our problem set-up:

public void declarePredicates() {
    declareClosedPredicate(new LivesPred());
    declareOpenPredicate(new KnowsPred());
}

Lives is a closed predicate since we know all atom values in advance. Knows is an open predicate since we want to infer the atom values. If we had any predicates with some known atom values and some unknown ones, those predicates would also be added as open predicates.

If we wanted to add a predicate that doesn't have its own TalkingPredicate class, we could do so via declareClosedPredicate("MyPred", 2); (where 2 is the predicate's arity).

We also add the PSL rules:

@Override
public void addInteractionRules() {
    addRule(new LivesToKnowsRule(this));
    addRule(new KnowsSymmetryConstraint(this));}

To add rules that aren't defined as TalkingRule classes, you can call addRule("MyRule", "MyPred(A,B) = MyPred(B,A).");.

The idea generator

The SampleIdeaGenerator contains the code for grounding the atoms. (Alternatively, this could also be done in the SamplePslProblem's pregenerateAtoms() method.)

In our example case, the idea generator reads a file containing information on the Lives atoms. We add those atoms and their fixed values to the problem's inference scope as observations (value is a double; person and address are strings):

super.pslProblem.addObservation(LivesPred.NAME, value, person, address);

We then also add atoms for every pair of possible arguments for Knows, as targets whose values we want to infer (person1 and person2 are strings):

super.pslProblem.addTarget(KnowsPred.NAME, person1, person2);

Putting it all together

The code for preparing and running the inference is in EntryClass.java:

We first create the classes in charge of managing the database partitions and saving the inference results:

        ProblemManager problemManager = ProblemManager.defaultProblemManager();
        DatabaseManager dbManager = problemManager.getDbManager();
        String problemId = problemManager.getNewId("SampleProblem");

Then declare the rules and predicates of our PSL problem and generate the ground atoms:

        SamplePslProblem problem = new SamplePslProblem(dbManager, problemId);
        SampleIdeaGenerator ideaGen = new SampleIdeaGenerator(problem);
        ideaGen.generateAtoms("/examples/livesknows/addresses.csv");

We then run the inference:

        problemManager.registerProblem(problemId, problem);
        InferenceLogger logger = new InferenceLogger();
        problemManager.preparePartitionsAndRun(Collections.singletonList(problem), logger);

...and extract the results:

        InferenceResult result = problemManager.getLastResult(problemId);
        RuleAtomGraph rag = result.getRag();

Graphical user interface

To visually explore the rule-atom graph and to read the verbalized explanations for the atom values, run the EntryClass in the examples.livesknows subpackage of psl-ragviewer.

Clone this wiki locally