Giter VIP home page Giter VIP logo

2p-kt's Introduction

2P-Kt

Some quick links:

Intro

The 2P logo

tuProlog (2P henceforth) is a multi-paradigm logic programming framework written in Java.

2P-Kt is a Kotlin-based and multi-platform reboot of 2P. It consists of an open ecosystem for Symbolic Artificial Intelligence (AI). For this reason, 2P-Kt consists of a number of incrementally inter-dependent modules aimed at supporting symbolic manipulation and reasoning in an extensible and flexible way.

A complete overview about modules and their dependencies is provided by the following diagram:

2P-Kt project map

As shown in the project map, 2P-Kt currently focuses on supporting knowledge representation and automatic reasoning through logic programming, by featuring:

  • a module for logic terms and clauses representation, namely core,

  • a module for logic unification representation, namely unify,

  • a module for in-memory indexing and storing logic theories, as well as other sorts of collections of logic clauses, namely theory,

  • a module providing generic API for resolution of logic queries, namely solve, coming with several implementations (e.g. solve-classic and solve-streams, targetting Prolog ISO Standard compliant resolution),

  • a module providing generic API for the probabilistic resolution of logic queries via probabilistic logic programming (PLP), namely solve-plp, coming with an implementation targetting ProbLog (solve-problog)

  • a module providing OR-concurrent resolution facilities, namely solve-concurrent,

  • a number of modules (i.e., the many dsl-* modules) supporting a Prolog-like, Domain Specific Language (DSL) aimed at bridging the logic programming with the Kotlin object-oriented & functional environment,

  • two parsing modules: one aimed at parsing terms, namely parser-core, and the other aimed at parsing theories, namely parser-theory,

  • two serialisation-related modules: one aimed at (de)serialising terms and clauses, namely serialize-core, and the other aimed at (de)serialising terms theories, namely serialize-theory,

  • a module for using Prolog via a command-line interface, namely repl,

  • a module for using Prolog via a graphical user interface (GUI), namely ide,

  • a module for using PLP (and, in particular, ProbLob) via a GUI, namely ide-plp.

The modular, unopinionated architecture of 2P-Kt is deliberately aimed at supporting and encouraging extensions towards other sorts of symbolic AI systems than Prolog---such as ASP, tabled-Prolog, concurrent LP, etc.

Furthermore, 2P-Kt is developed as in pure, multi-platform Kotlin project. This brings two immediate advantages:

  1. it virtually supports several platforms, there including JVM, JS, Android, and Native (even if, currently, only JVM, JS and Android are supported),
  2. it consists of a very minimal and lightweight library, only leveraging on the Kotlin common library, as it cannot commit to any particular platform standard library.

Users

2P-Kt can either be used as a command-line program or as a Kotlin, JVM, Android, or JS library.

The 2P-Kt executables are currently available for download on the Releases section of the GitHub repository.

The 2P-Kt modules for JVM, Android, or Kotlin users are currently available for import on Maven Central, under the it.unibo.tuprolog group ID (not to be confused with the it.unibo.alice.tuprolog, which contains the old Java-based implementation). The same modules are available through an ad-hoc Maven repository as well, hosted by GitHub.

The 2P-Kt modules for JS users, are available for import on NPM, under the @tuprolog organization.

End users

Graphical User Interface

If you need a GUI for your Prolog interpreter, you can rely on the 2P-Kt IDE which is available on the Releases section of the GitHub repository.

The page of the latest release of 2P-Kt exposes a number of Assets. There, the one named:

2p-ide-VERSION-redist.jar

is the self-contained, executable Jar containing the 2P-Kt-based Prolog interpreter (VERSION may vary depending on the actual release version).

After you download the 2p-ide-VERSION-redist.jar, you can simply launch it by running:

java -jar 2p-ide-VERSION-redist.jar

However, if you have properly configured the JVM on your system, it may be sufficient to just double-click on the aforementioned JAR to start the IDE. In any case, running the JAR should make the following window appear:

A screenshot of the 2P-Kt IDE

There, one may query the 2P-Kt Prolog interpreter against the currently opened theory file, which can of course be loaded from the user's file system by pressing File and then Open....

To issue a query, the user must write it in the query text field, at the center of the application. By either pressing Enter while the cursor is on the query text field, or by clicking on the Solve (resp. Solve all) button, the user can start a new resolution process, aimed at solving the provided query. Further solutions can be explored by clicking on the Next (resp. All next) button over and over again. The Next (resp. All next) button shall appear in place of Solve (resp. Solve all) if and when further solutions are available for the current query.

One may also compute all the unexplored solutions at once by clicking on the Solve all (resp. All next) button. Avoid this option in case of your query is expected to compute an unlimited amount of solutions.

To perform a novel query, they user may either:

  • write the new query in the query text field, and then press Enter, or
  • click on the Stop button, write the new query in the query text field, and then press the Solve (resp. SolveNext) button again.

The Reset button cleans up the status of the solver, clearing any side effect possibly provoked by previous queries (including assertions, retractions, prints, warnings, loading of libraries, operators, or flags).

Finally, users may inspect the current status of the solver by leveraging the many tabs laying at the bottom of the IDE. There,

  • the Solutions tab is aimed at showing the Prolog interpreter's answers to the user's queries;
  • the Stdin tab is aimed at letting the user provide some text the Prolog interpreter's standard input stream;
  • the Stdout tab is aimed at showing the Prolog interpreter's standard output stream;
  • the Stderr tab is aimed at showing the Prolog interpreter's standard error stream;
  • the Warnings tab is aimed at showing any warning possibly generated by the Prolog interpreter while computing;
  • the Operators tab is aimed at showing the current content Prolog interpreter's operator table;
  • the Flags tab is aimed showing the actual values of all the flags currently defined with the Prolog interpreter;
  • the Libraries tab is aimed at letting the user inspect the currently loaded libraries and the predicates, operators, and functions they import;
  • the Static (resp. Dynamic) KB tab is aimed at letting the user inspect the current content of the Prolog interpreter's static (resp. dynamic) knowledge base.

Any of these tabs may be automatically updated after a solution to some query is computed. Whenever something changes w.r.t. the previous content of the tab, an asterisk will appear close to the tab name, to notify an update in that tab.

Command Line Interface

If you just need a command-line Prolog interpreter, you can rely on the 2P-Kt REPL which is available on the Releases section of the GitHub repository.

The page of the latest release of 2P-Kt exposes a number of Assets. There, the one named:

2p-repl-VERSION-redist.jar

is the self-contained, executable Jar containing the 2P-Kt-based Prolog interpreter (VERSION may vary depending on the actual release version).

After you download the 2p-repl-VERSION-redist.jar, you can simply launch it by running:

java -jar 2p-repl-VERSION-redist.jar

This should start an interactive read-eval-print loop accepting Prolog queries. A normal output should be as follows:

# 2P-Kt version LAST_VERSION_HERE

?- <write your dot-terminated Prolog query here>.

For instance:

A screenshot of the 2P-Kt CLI

Other options or modes of execution are supported. One can explore them via the program help, which can be displayed by running:

java -jar 2p-repl-VERSION-redist.jar --help

This should display a message similar to the following one:

Usage: java -jar 2p-repl.jar [OPTIONS] COMMAND [ARGS]...

  Start a Prolog Read-Eval-Print loop

Options:
  -T, --theory TEXT  Path of theory file to be loaded
  -t, --timeout INT  Maximum amount of time for computing a solution (default:
                     1000 ms)
  --oop              Loads the OOP library
  -h, --help         Show this message and exit

Commands:
  solve  Compute a particular query and then terminate

Gradle users

To import the 2P-Kt module named 2P_MODULE (version 2P_VERSION) into your Kotlin-based project leveraging on Gradle, you simply need to declare the corresponding dependency in your build.gradle(.kts) file:

// assumes Gradle's Kotlin DSL
dependencies {
   implementation("it.unibo.tuprolog", "2P_MODULE", "2P_VERSION")
}

In this way, the dependencies of 2P_MODULE should be automatically imported.

The step above, requires you to tell Gradle to either use Maven Central or our GitHub repository (or both) as a source for dependency lookup. You can do it as follows:

// assumes Gradle's Kotlin DSL
repositories {
    maven("https://maven.pkg.github.com/tuProlog/2p-kt")
    mavenCentral()
}

Authentication may be required in case the GitHub repository is exploited

JVM-only projects with Gradle

Remember to add the -jvm suffix to 2P_MODULE in case your project only targets the JVM platform:

// assumes Gradle's Kotlin DSL
dependencies {
   implementation("it.unibo.tuprolog", "2P_MODULE-jvm", "2P_VERSION")
}

Maven users

To import the 2P-Kt module named 2P_MODULE (version 2P_VERSION) into your Kotlin-based project leveraging on Maven, you simply need to declare the corresponding dependency in your pom.xml file:

<dependency>
   <groupId>it.unibo.tuprolog</groupId>
   <artifactId>2P_MODULE</artifactId>
   <version>2P_VERSION</version>
</dependency>

In this way, the dependencies of 2P_MODULE should be automatically imported.

The step above, requires you to tell Maven to either use Maven Central or our GitHub repository (or both) as a source for dependency lookup. You can do it as follows:

<repositories>
    <repository>
        <id>github-2p-repo</id>
        <url>https://maven.pkg.github.com/tuProlog/2p-kt</url>
    </repository>
</repositories>

Authentication may be required in case the GitHub repository is exploited

JVM-only projects with Maven

Remember to add the -jvm suffix to 2P_MODULE in case your project only targets the JVM platform:

<dependency>
   <groupId>it.unibo.tuprolog</groupId>
   <artifactId>2P_MODULE-jvm</artifactId>
   <version>2P_VERSION</version>
</dependency>

NPM users (JavaScript-only projects)

The 2P-Kt software is available as a JavaScript library as well, on NPM, under the @tuprolog organization. To import the 2P_MODULE into your package.json, it is sufficient to declare your dependency as follows:

{
  "dependencies": {
    "@tuprolog/2P_MODULE": "^2P_MODULE_VERSION"
  }
}

Notice that the JS dependencies of 2P_MODULE should be automatically imported.

Developers

Working with the 2P-Kt codebase requires a number of tools to be installed and properly configured on your system:

  • JDK 11+ (please ensure the JAVA_HOME environment variable is properly) configured
  • Kotlin 1.5.10+
  • Gradle 7.1+ (please ensure the GRADLE_HOME environment variable is properly configured)
  • Git 2.20+

Develop 2P-Kt with IntelliJ Idea

To participate in the development of 2P-Kt, we suggest the IntelliJ Idea IDE. The free, Community version will be fine.

Recommended configuration

You will need the Kotlin plugin for IntelliJ Idea. This is usually installed upon Idea's very first setup wizard. However, one may easily late-install such plugin through the IDE's Plugins settings dialog. To open such dialog, use Ctrl+Shift+A, then search for "Plugins"

Importing the project

  1. Clone this repository in a folder of your preference using git clone appropriately

  2. Open IntellJ Idea. If a project opens automatically, select "Close project". You should be on the welcome screen of IntelliJ idea, with an aspect similar to this image: IntelliJ Welcome Screen

  3. Select "Import Project"

  4. Navigate your file system and find the folder where you cloned the repository. Do not select it. Open the folder, and you should find a lowercase 2p-in-kotlin folder. That is the correct project folder, created by git in case you cloned without specifying a different folder name. Once the correct folder has been selected, click Ok

  5. Select "Import Project from external model"

  6. Make sure "Gradle" is selected as external model tool

  7. Click Finish

  8. If prompted to override any .idea file, try to answer No. It's possible that IntelliJ refuses to proceed, in which case click Finish again, then select Yes

  9. A dialog stating that "IntelliJ IDEA found a Gradle build script" may appear, in such case answer Import Gradle Project

  10. Wait for the IDE to import the project from Gradle. The process may take several minutes, due to the amount of dependencies. Should the synchronization fail, make sure that the IDE's Gradle is configured correctly:

  11. In 'Settings -> Build, Execution, Deployment -> Build Tools > Gradle', for the option 'Use Gradle from' select 'gradle-wrapper.properties file'. Enabling auto-import is also recommended

Developing the project

Contributions to this project are welcome. Just some rules:

  • We use git flow, so if you write new features, please do so in a separate feature/ branch

  • We recommend forking the project, developing your stuff, then contributing back via pull request directly from the Web interface

  • Commit often. Do not throw pull requests with a single giant commit adding or changing the whole world. Split it in multiple commits and request a merge to the mainline often

  • Stay in sync with the develop branch: pull often from develop (if the build passes), so that you don't diverge too much from the main development line

  • Do not introduce low quality or untested code. Merge requests will be reviewed before merge.

Building the project

While developing, you can rely on IntelliJ to build the project, it will generally do a very good job. If you want to generate the artifacts, you can rely on Gradle. Just point a terminal on the project's root and issue

./gradlew build

This will trigger the creation of the artifacts the executions of the tests, the generation of the documentation and of the project reports.

Versioning

The 2P project leverages on Semantic Versioning (SemVer, henceforth).

In particular, SemVer is enforced by the current Gradle configuration, which features DanySK's Git sensitive SemVer Gradle Plugin. This implies it is strictly forbidden in this project to create tags whose label is not a valid SemVar string.

Notice that the 2P project is still in its initial development stage---as proven by the major number equal to 0 in its version string. According to SemVer, this implies anything may change at any time, as the public API should not be considered stable.

Issue tracking

If you meet some problem in using or developing 2P, you are encouraged to signal it through the project "Issues" section on GitHub.

2p-kt's People

Contributors

davidegreco avatar forrestone avatar gciatto avatar gilbocc avatar giordo94 avatar jasondellaluce avatar lanzonisilvia avatar lrizzato avatar mannbonn avatar paoloverdini1997 avatar renovate-bot avatar renovate[bot] avatar semantic-release-bot avatar siboxd avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

2p-kt's Issues

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

Open

These updates have all been created already. Click a checkbox below to force a retry/rebase of any.

Detected dependencies

github-actions
.github/workflows/build-and-deploy.yml
  • DanySK/are-secrets-available 1.0.0
  • actions/checkout v4.1.4
  • nicolasfara/precompute-semantic-release-version-action 1.0.2
  • DanySK/build-check-deploy-gradle-action 2.4.18
  • DanySK/action-checkout 0.2.16
  • DanySK/build-check-deploy-gradle-action 2.4.18
  • actions/checkout v4.1.4
  • DanySK/build-check-deploy-gradle-action 2.4.18
  • actions/checkout v4.1.4
  • actions/setup-node v4.0.2
  • DanySK/build-check-deploy-gradle-action 2.4.18
  • ubuntu 22.04
  • ubuntu 22.04
  • ubuntu 22.04
  • ubuntu 22.04
  • ubuntu 22.04
  • ubuntu 22.04
.github/workflows/dispatcher.yml
  • ubuntu 22.04
.github/workflows/test-extensively.yml
  • DanySK/action-checkout 0.2.16
  • DanySK/build-check-deploy-gradle-action 2.4.18
  • DanySK/action-checkout 0.2.16
  • DanySK/build-check-deploy-gradle-action 2.4.18
gitlabci
.gitlab-ci.yml
gradle
gradle.properties
settings.gradle.kts
  • com.gradle.enterprise 3.17.2
  • org.danilopianini.gradle-pre-commit-git-hooks 2.0.4
  • org.gradle.toolchains.foojay-resolver-convention 0.8.0
build.gradle.kts
bdd/gradle.properties
bdd/build.gradle.kts
core/gradle.properties
core/build.gradle.kts
datalog/gradle.properties
datalog/build.gradle.kts
documentation/gradle.properties
documentation/build.gradle.kts
dsl-core/gradle.properties
dsl-core/build.gradle.kts
dsl-solve/gradle.properties
dsl-solve/build.gradle.kts
dsl-theory/gradle.properties
dsl-theory/build.gradle.kts
dsl-unify/gradle.properties
dsl-unify/build.gradle.kts
full/gradle.properties
full/build.gradle.kts
gradle/libs.versions.toml
  • org.openjfx:javafx-base 19.0.2.1
  • org.openjfx:javafx-fxml 19.0.2.1
  • org.openjfx:javafx-controls 19.0.2.1
  • org.openjfx:javafx-graphics 19.0.2.1
  • com.fasterxml.jackson.core:jackson-core 2.17.1
  • com.fasterxml.jackson.dataformat:jackson-dataformat-xml 2.17.1
  • com.fasterxml.jackson.dataformat:jackson-dataformat-yaml 2.17.1
  • com.fasterxml.jackson.datatype:jackson-datatype-jsr310 2.17.1
  • com.github.ajalt.clikt:clikt 3.5.4
  • guru.nidi:graphviz-java 0.18.1
  • io.github.gciatto:kt-math 0.10.0
  • io.github.javaeden.orchid:OrchidDocs 0.21.1
  • io.github.javaeden.orchid:OrchidKotlindoc 0.21.1
  • io.github.javaeden.orchid:OrchidPluginDocs 0.21.1
  • net.sourceforge.plantuml:plantuml 1.2024.4
  • org.antlr:antlr4 4.13.1
  • org.antlr:antlr4-runtime 4.13.1
  • org.fxmisc.richtext:richtextfx 0.11.2
  • org.jetbrains.kotlinx:kotlinx-coroutines-core 1.6.4
  • org.jetbrains.kotlinx:kotlinx-coroutines-test 1.6.4
  • org.jetbrains.kotlin:kotlin-stdlib 1.9.23
  • org.jetbrains.kotlin:kotlin-stdlib-jdk8 1.9.23
  • org.jetbrains.kotlin:kotlin-stdlib-js 1.9.23
  • org.jetbrains.kotlin:kotlin-reflect 1.9.23
  • org.jetbrains.kotlin.jvm 1.9.23
  • org.danilopianini.git-sensitive-semantic-versioning 3.1.4
  • io.github.gciatto.kt-mpp.bug-finder 4.1.3
  • io.github.gciatto.kt-mpp.documentation 4.1.3
  • io.github.gciatto.kt-mpp.linter 4.1.3
  • io.github.gciatto.kt-mpp.maven-publish 4.1.3
  • io.github.gciatto.kt-mpp.npm-publish 4.1.3
  • io.github.gciatto.kt-mpp.versions 4.1.3
  • io.github.gciatto.kt-mpp.js-only 4.1.3
  • io.github.gciatto.kt-mpp.jvm-only 4.1.3
  • io.github.gciatto.kt-mpp.multiplatform 4.1.3
  • io.github.gciatto.kt-mpp.multi-project-helper 4.1.3
  • io.github.gciatto.kt-mpp.fat-jar 4.1.3
  • io.github.gciatto.gradle-mock-service 1.0.8
  • com.eden.orchidPlugin 0.21.1
ide/gradle.properties
ide/build.gradle.kts
ide-plp/gradle.properties
ide-plp/build.gradle.kts
io-lib/gradle.properties
io-lib/build.gradle.kts
oop-lib/gradle.properties
oop-lib/build.gradle.kts
parser-core/gradle.properties
parser-core/build.gradle.kts
parser-js/gradle.properties
parser-js/build.gradle.kts
parser-jvm/gradle.properties
parser-jvm/build.gradle.kts
parser-theory/gradle.properties
parser-theory/build.gradle.kts
repl/gradle.properties
repl/build.gradle.kts
serialize-core/gradle.properties
serialize-core/build.gradle.kts
serialize-theory/gradle.properties
serialize-theory/build.gradle.kts
solve/gradle.properties
solve/build.gradle.kts
solve-classic/gradle.properties
solve-classic/build.gradle.kts
solve-concurrent/gradle.properties
solve-concurrent/build.gradle.kts
solve-plp/gradle.properties
solve-plp/build.gradle.kts
solve-problog/gradle.properties
solve-problog/build.gradle.kts
solve-streams/gradle.properties
solve-streams/build.gradle.kts
test-dsl/gradle.properties
test-dsl/build.gradle.kts
test-solve/gradle.properties
test-solve/build.gradle.kts
theory/gradle.properties
theory/build.gradle.kts
unify/gradle.properties
unify/build.gradle.kts
utils/gradle.properties
utils/build.gradle.kts
gradle-wrapper
gradle/wrapper/gradle-wrapper.properties
  • gradle 8.7
npm
package.json
  • semantic-release-preconfigured-conventional-commits 1.1.85
  • node 20.12

  • Check this box to trigger a request for Renovate to run again on this repository

Example for reading prolog file, adding a clause and unifying

I am looking for an example of how to

  • Load a Prolog script,
  • Add few more facts to the load program programmatically
  • Find a solution to a resulting predicate

In details, let say I have a script main.pl:

increment(A, B) :-
    diff(C),
    B is A + C.   

Then I'd like to enhance it on Java side

Struct.of("diff", Integer.of(15));

and then run

Struct.of("increment", Atom.of(15), Integer.of("X"));

... something that the returns the value of X as an int

Unification algorithm efficiency

The actual implementation of the unification algorithm was made following the first definition in this paper.

But after Section 3 in this paper, more efficient versions of the algorithm are proposed; in some future, will be a good thing to implement the more efficient version proposed.

Using 2p-kt programmatically

I wanted to use the library programmatically from Java. Sadly I couldn't find any specific information regarding this in the readme.md or in the projects documentation (the respective page is empty).

So my questions are:

  1. Where is the programmatic entry point for adding a knowledge base and then querying the Prolog engine against this knowledge base. I have the knowledge base and the respective queries as a string representation.
  2. Which part of the library do I need for that (core, parser, theory, ...?)

Clause isWellFormed

Actual implementation of Clauses sintactically allows any Term as its body, but the Standard Prolog states that:

A well-formed body cannot contain a number.

So it would be nice to add a property isWellFormed to Clause that will detect if it's well formed or not.

Sets modelling is incorrect

Sadly, I modeled sets in a wrong way.

Currently, sets are modeled as follows:

  • N-Sized Set -> '{}'(Arg1, ..., ArgN), i.e., a struct whose functor is {} and whose arguments are the set items
    • for all N = 0, 1, 2, ...
  • the Empty Set is actually an Atom

But actually, their are modeled differently in Prolog:

  • Empty set -> Atom '{}'
  • 1-sized set with argument X -> Struct '{}'(X)
  • N-sized set with arguments X1, ..., Xn -> Struct '{}'(X) where X = (X1, ..., Xn) is a conjunction

So, for instance, unifying { 1, 2, 3 } with { 1, X }, is solved by X = (2, 3)

Terms passing trough Scope should "absorb" variable bindings

I think that all Terms manipulated inside a Scope should absorb current variable bindings.

Actual situation:

    val scope = Scope.of("X")

    val scopeExternalStructWithXVariable = Struct.of("f", Var.of("X"))

    println("Two different variables with same name should only be equals.")
    println(scope["X"] === scopeExternalStructWithXVariable[0]) // false, correct
    println(scope["X"] == scopeExternalStructWithXVariable[0]) // true, correct
    println(scope["X"]?.strictlyEquals(scopeExternalStructWithXVariable[0])) // false, correct

    val myFactCreatedTroughXScope = with(scope){
        factOf(scopeExternalStructWithXVariable)
    }

    val structWithXVariableThatWalkedTroughXScope = myFactCreatedTroughXScope.head

    println("\nExternal terms manipulated inside a scope, doesn't absorb variable bindings; is that misleading for final users?")
    println(scope["X"] === structWithXVariableThatWalkedTroughXScope[0]) // false, maybe *expected* true by an API user??
    println(scope["X"] == structWithXVariableThatWalkedTroughXScope[0]) // true, correct
    println(scope["X"]?.strictlyEquals(structWithXVariableThatWalkedTroughXScope[0])) // false, maybe *expected* true by an API user??

Expected:

In my opinion all three last println should print true!

KotlinTest testing suite

KotlinTest is a powerful testing suite that could be adopted to make project tests more readable and expressive.

Among other feature it supports Property Based testing and Data Driven testing, two important features to be more declarative in testing.

Adopting this suite will mean migrating to JUnit 5 and therefore also rewriting part of the existing test structure to adapt to it.

Unifier.mgu infinite loop

I've found a problem that makes actual mgu implementation to go into infinite loop.

Steps to reproduce:

  1. Execute this code somewhere:
fun main() {
    val myBuggyUnifier = Unifier.naive(Substitution.of("X", Atom.of("a")))

    myBuggyUnifier.mgu(Atom.of("b"), Var.of("X"))
    
    println("You'll never see this printed, until issue solved")
}

Expected behaviour: mgu should return null and the println should be executed!

Run JavaScript tests

Common code of each sub-project, should run tests on each target platform.

Running tests on JavaScript platform is not out-of-the-box like JVM testing.

A custom way to run tests over a JavaScript test framework should be found and integrated into the project.

Apply DRY principle in build.gradle files

All of the build.gradle.kts files now share an identical part that should be moved in a common place, to make changes consistent within all subprojects.

For example there is already an inconsistency between the multiplatform plugin version in core subproject (1.3.31) and other subprojects (1.3.21); to avoid this, the common part could be placed in project root gradle file.

Code Coverage reporting

Should be added a test code coverage tool to detect, in an automatic fashion, which parts of code are still not adequately tested.

Build failing because tests are failing

There are three jvm tests in TestReal class that are failing; their names are: creationAsNumbers2, comparisons, value.

This failure shold be investigated and solved.

Indicator interface

There is yet another notable sort of structures that may deserve their own interface in core: indicators.
A well formed indicator is a struct in the form '/'(F, N) where F is an atom and N is a non negative integer.
Of course, variables may be accepted too in some cases.

Unification bug, preventing it from termination

I've found a BUG in the implementation of AbstractUnificationStrategy, in particular the implementation of applySubstitutionToEquations(...)

The bug prevents simple unification like (f(a,b(X)) :- b) = (f(A,B) :- C) from being "solved", making the Unification strategy go into infinite loop.

The problem is due the use of === (aka referential equality) that is used to check if changes are made during Substitution application; in fact applying a Substitution always results in creation of new Struct for example (because of the support to immutability); so even if actually no change is made, the function returns true signaling changes and making the algorithm continue indefinitely.

The solution could be simply the change of referential equality with simple equality check, in that function.

Furthermore, a new test for this unification should be added to prevent future regression.

Substitution.Success is a Unifier

Since in this paper is stated

A solution of an equation, called a unifier, is any substitution ฯ‘, if it exists, which makes the two terms identical

So a Success Substitution should be called Unifier

Solve module: Signature vararg doesn't specify position

I think that Signature is missing that in kotlin vararg parameters can be in all positions of a method signature, not only in last position like in java.

So if vararg support is for invoking even kotlin vararg methods, this class should be enhanched with a position of the vararg.

List.from and List.of should create different type of lists

There are two types of lists:

  • those terminated with an empty list terminator, also writeable as [E1, E2, E3]
  • those terminated with pipe operator [H1, H2 | T]

For creating lists now we have two factory methods in List companion object:

  • List.of: creates always an empty terminated list with given arguments (e.g the first type described above)
  • List.from: that can create both list types passing, or not, a terminal Term to the method

The second method should always create a "piped list" with last item, and leave the creation of empty terminated lists to List.of only, to better separate their responsibilities (Single Responsibility Principle)

Untested core changes

Some changes were made to core without testing them, and therefore:

  • Test the addition of method Substition.of(Iterable<Pair<Var, Term>>)
  • Test the addition of Constant interface
  • Test the addition of Term.isConstant property

Tests should be aligned.

Add some documentation comments

In code there's no documtation, it would be better to document at least non-trivial/domain-specific "things" (methods, properties and classes) to shortly explain what they stand for.

Total ordering of Terms

According to section 2.1.2 (page 6) of the Prolog standard, terms are totally ordered in a non-trivial way:

In Standard Prolog the terms are totally ordered according to the following rules which define the binary relation term_precedes.

More precisely:

  • All variables precede all floating-point numbers, which precede all integers, which precede all atoms, which precede all compound terms.
  • All variables are totally ordered but the order is implementation dependent and may vary during computations.
  • All numbers (of the same type) are ordered according to the usual arithmetic.
  • All atoms are ordered according to the characters forming their name and the character codes of the characters (an integer associated with each character). The null atom precedes all non-null atoms (hence z precedes z_a). An atom a1 precedes an atom a2 if the character code of the first character of a1 is less than the character code of the first character of a2. If these
    values are equal, the tail character sequences are compared. Notice that the order induced by the character codes is implementation
    defined, except that the alphabetical order must be respected and the digits must be sequential. More details are provided in Section 9.1.3.
  • Compound terms are ordered according to their arity; if they have same arity, by the name of their functor. If they have same arity and same functor name, they are ordered by considering successively the order of their arguments starting from the first.
    • Example: g(X) precedes f(X, V), f(Z,b) precedes f(a,A), according to the ASCII table given in Annex 11.3, but is after f(Z, a). f(Z, Z) precedes f(X, X, X) but its comparison with f(X, X) depends on the processor (implementation dependent).

TuprologExceptions

In core subproject there are two exceptions TuprologException and TuprologRuntimeException that extend the same base class Exception.

Why they're not exteding one from each other? And why there are two exceptions?

In Kotlin all exceptions are unchecked an so in my opinion there's no need for a TuprologException (checked exceptions only raise management problems, when used in Java, with no true benefit)

freshCopy() and same named variables

The method freshCopy() for now doesn't take into account that if a Variable occurs multiple times in a Term, then the same copied instance should be placed multiple times in the copy, to avoid creating diverging variables.

Substitution related method names

I think that more meaningful names should be given to methods that work with Substitutions

For example:

  1. In Substitution interface the method ground(term: Term) could be named substituteIn(term: Term) or applyIn(term: Term)
  2. In Term interface the methods groundTo(substitution: Substitution) could be named simply apply(substitution: Substitution)

Code before:

val substitutedTerm = myStruct.groundTo(mySubstitution)
val substitutedTerm = mySubstitution.ground(myStruct)

Code after:

val substitutedTerm = myStruct.apply(mySubstitution)
val substitutedTerm = mySubstitution.applyIn(myStruct) // mySubstitution.substituteIn(myStruct)

Substitution should be a type, not a type alias

Substitution should be an interface, with maybe two implementations (to be studied how can be done):

  • an implementation SuccessSubstitution backed by a Map<Var, Term> with actual substitutions
  • a singleton implementation FailedSubstitution that will be "empty"

Rename Scope.listOf to Scope.logicListOf

Using A scope with method listOf is misleading to Kotlin user that really wanted to create a kotlin List.

Methods such as listOf() and setOf() that clash with library method names should be changed to logicListOf() and logicSetOf().

Currently if you want to construct a tuple with tupleOf(Iterable<Term>) you can't write tupleOf(listOf(atomOf("a"), atomOf("b"))) inside Scope scope, because the outermost argument is considered as a logic list, so it is one Term an so Tuple constructor complains throwing exception... But the user didn't want to create a logical list.

NullPointerException when saving a file.

This popup shows up when trying to save a file.
image

Steps to reproduce:

  • create a new file
  • click File > Save As
  • save the file with .pl o .pro extension
  • click File > Save

Versions:

  • 2P-Kt v0.20.4-dev61+cbec54e9
  • JVM v17.0.3
  • JavaFX v19-ea+6
  • Kubuntu 21.10

IDE: output box contents should be made copyable

In the 2p-kt IDE, the content of the solution box (and most likely of other similar boxes) cannot be copied for later pasting elsewhere. This is a pity, copying solutions (but also output, etc.) is often needed for documentation, debug, reports, etc.
Should be fixed in one of the following ways:

  • preferred approach: simply enable text selection and copying, in a standard way; OR
  • support mouse right-click with contextual menu providing a "copy" item; OR
  • just add a "copy" button (less general, somehow weird, but perhaps quicker to impement..?)

strictlyEquals should check specific instance class

Now method stricltyEquals checks if passed class is of one type or a subclass of it.

As it should be a strict check, we maybe should add a check to limit correct classes to the specific instance class.

Hash maps should be used in RETE

Hashmaps are not currently used in it.unibo.tuprolog.theory.Rete but they should in order to reach better performances in accessing clauses

RETE indexes should be finely tested

It is of paramount importance that RETE indexes (i.e. it.unibo.tuprolog.theory.ReteTree) are finely tested in order to ensure each method work as intended.
Thus we should not merely test RETE indexes indirectly by testing ClauseDatabases.

Let me here remark the intended meaning of the ReteTree API:

  • ReteTree.Companion.of should create a new tree out of the provided Clauses

    • The partial order among the provided clauses sharing the same functor & arity is of paramount importance
      • Such order affect the behaviour of ReteTree.get, ReteTree.clauses, ReteTree.remove
  • ReteTree.clauses: Sequence<Clause> should return a sequence lazily iterating over all the clauses indexed by the current tree, there including directives.

    • An essential feature is that the partial ordering of clauses sharing the same functor & arity (w.r.t. insertion order) must be respected in the sequence.
  • ReteTree.get(Clause): Sequence<Clause> should return a sequence lazily iterating over all the clauses indexed by the current tree which unify with Clause according to the default unifier.

    • An essential feature is that the partial ordering of clauses sharing the same functor & arity (w.r.t. insertion order) must be respected in the sequence.
  • ReteTree.remove(Clause, Int): Sequence<Clause> should return a sequence lazily iterating over all the clauses indexed by the current tree which unify with Clause according to the default unifier. In case the second argument, namely limit, is negative, then all possible Clauses are returned within the sequence. Otherwise, if limit is non-negative the amount of Clauses actually returned by this method must be lower or equals to limit. In any case, all Clauses returned within the output sequence are removed from the current ReteTree and its internal nodes.

    • An essential feature is that the partial ordering of clauses sharing the same functor & arity (w.r.t. insertion order) must be respected.
  • ReteTree.clone(): ReteTree<*> produces a brand new deep copy of the current index---except for the indexed Clauses, for which only references are cloned. Each edit to returned index does not affect the original index.

ClauseDatabase clauses' checks

From the Prolog Standard:

In a database all the clauses are well-formed (Section 2.2.3) and transformed, i.e. all variables X in the position of a predication in the body of the clauses have been replaced by call(X) and all the facts also have the form of a rule whose body is true. Thus all the clauses in a database have the form of a rule.

For example, the Clause product (A) : - A, A is stored in the database, after preparation for execution, as the term: product(A) :- call(A) , call(A)

Those transformation should be made during Clause addition and/or Database creation.

A check on Clause insertion should be added, to ensure that all contained clauses are well-formed

Support for user defined predicate true/1

Hello,

First of all, thanks for a great portable Prolog implementation.

I'm experimenting with reasoning in GDL. It's a subset of Prolog that is used to specify games for an AI to play.

The issue is that it requires the ability to assert some statements about a game using true/1 predicate, but it seems like even a simple example can't be handled by tuProlog.

If I create a file example.pdb with the following contents:

true(_).

I'm getting the output:

# 2P-Kt version 0.18.1
# Error while parsing theory file: example.pdb
#     Message: mismatched input '(' expecting {SIGN, ',', '|', FULL_STOP, OPERATOR}
#     Line   : 1
#     Column : 5
#     Clause : 0

# Successfully loaded library `prolog.io`
# Successfully loaded library `prolog.lang`

?- 

I'm using 2p-repl-0.18.1-redist.jar.

Can this issue be somehow solved?

Anonymous variables in unification

Anonymous variables during the unification process should be considered different even using the non-strict identity matcher.

They should always be considered different from each other unless they are the same instance.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.