# \[ANN\] Julog.jl - Prolog-style logic programming in Julia

**URL:** <https://discourse.julialang.org/t/ann-julog-jl-prolog-style-logic-programming-in-julia/35400>\
**Category:** Package Announcements\
**Tags:** package, announcement\
**Created:** [March 2, 2020, 12:54pm UTC](https://discourse.julialang.org/t/ann-julog-jl-prolog-style-logic-programming-in-julia/35400 "2020-03-02T12:54:41Z")\
**Posts on this page:** 14\
**Page:** 1

<div class="post-metadata">

**Author:** ![Xuan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/xuan/32/22100_2.png) [@Xuan](https://discourse.julialang.org/u/Xuan)\
**Post date:** [March 2, 2020, 12:54pm UTC](https://discourse.julialang.org/t/ann-julog-jl-prolog-style-logic-programming-in-julia/35400/1 "2020-03-02T12:54:41Z")

</div>

For anyone who’s wished they could do logic programming in Julia (but, like me, found [miniKanren](https://github.com/latticetower/MuKanren.jl) difficult to learn), I’m excited to announce [Julog.jl](https://github.com/ztangent/Julog.jl), a package for Prolog-style logic programming in Julia! It just got added to the registry, so you can install it with:

`] add Julog`

It’s called `Julog.jl` rather than `Prolog.jl` because it doesn’t exactly implement Prolog syntax or semantics, but it’s very close, and also allows for tight integration with standard Julia code. Some neat features include:

- Prolog-like syntax
- Interpolation of expressions
- Evaluation of custom Julia functions

## Example

The `@julog` macro can be used to create logical terms or Horn clauses using Prolog-like syntax. It can also applied to a list of clauses to create a knowledge base. For example, the traditional [Zen lineage chart](https://en.wikipedia.org/wiki/Zen_lineage_charts) can be encoded as:

```julia
clauses = @julog [
  ancestor(sakyamuni, bodhidharma) <<= true,
  teacher(bodhidharma, huike) <<= true,
  teacher(huike, sengcan) <<= true,
  teacher(sengcan, daoxin) <<= true,
  teacher(daoxin, hongren) <<= true,
  teacher(hongren, huineng) <<= true,
  ancestor(A, B) <<= teacher(A, B),
  ancestor(A, C) <<= teacher(B, C) & ancestor(A, B),
  grandteacher(A, C) <<= teacher(A, B) & teacher(B, C)
]

```

We can then query the knowledge base via [SLD resolution](https://en.wikipedia.org/wiki/SLD_resolution):

> **Query: Is Sakyamuni the dharma ancestor of Huineng?**
>
> ```julia
> julia> sat, subst = resolve(@julog(ancestor(sakyamuni, huineng)), clauses);
> julia> sat
> true
> 
> ```

> **Query: Who are the grandteachers of whom?**
>
> ```julia
> julia> sat, subst = resolve(@julog(grandteacher(X, Y)), clauses);
> julia> subst
> 4-element Array{Any,1}:
> {Y => sengcan, X => bodhidharma}
> {Y => daoxin, X => huike}
> {Y => hongren, X => sengcan}
> {Y => huineng, X => daoxin}
> 
> ```

If you _really_ like Prolog syntax, you can also use the `@prolog` macro on a string that contains a Prolog program, which will then be parsed to a list of Julog clauses. However, not all Prolog syntax is currently supported, though the subset corresponding to [Datalog](https://en.wikipedia.org/wiki/Datalog) should work fine.

## Interpolation

You can interpolate Julia expressions when constructing Julog terms using the `@julog` macro. Julog supports two forms of interpolation. The first form is constant interpolation using the `$` operator, where ordinary Julia expressions are converted to `Const`s:

> **Interpolating Julia expressions**
>
> ```julia
> julia> e = exp(1)
> 2.718281828459045
> julia> term = @julog irrational($e)
> irrational(2.718281828459045)
> julia> dump(term)
> Compound
> name: Symbol irrational
> args: Array{Term}((1,))
> 1: Const
> name: Float64 2.718281828459045
> 
> ```

The second form is term interpolation using the `:` operator, where pre-constructed Julog terms are interpolated into a surrounding Julog expression:

> **Interpolating Julog terms**
>
> ```julia
> julia> e = Const(exp(1))
> 2.718281828459045
> julia> term = @julog irrational(:e)
> irrational(2.718281828459045)
> julia> dump(term)
> Compound
> name: Symbol irrational
> args: Array{Term}((1,))
> 1: Const
> name: Float64 2.718281828459045
> 
> ```

Interpolation allows us to easily generate Julog knowledge bases programatically using Julia code:

```julia
julia> people = @julog [avery, bailey, casey, darcy];
julia> heights = [@julog(height(:p, cm($(rand(140:200))))) for p in people]
4-element Array{Compound,1}:
 height(avery, cm(155))
 height(bailey, cm(198))
 height(casey, cm(161))
 height(darcy, cm(175))

```

## Custom Functions

In addition to standard arithmetic functions, `Julog` supports the evaluation of custom functions during proof search, allowing users to leverage the full power of precompiled Julia code. This can be done by providing a dictionary of functions when calling `resolve`. This dictionary can also accept constants (allowing one to store, e.g., numeric-valued fluents), and lookup-tables. E.g.:

> **Example custom functions**
>
> ```julia
> funcs = Dict()
> funcs[:pi] = pi
> funcs[:sin] = sin
> funcs[:cos] = cos
> funcs[:square] = x -> x * x
> funcs[:lookup] = Dict((:foo,) => "hello", (:bar,) => "world")
> 
> @assert resolve(@julog(sin(pi / 2) == 1), Clause[], funcs=funcs)[1] == true
> @assert resolve(@julog(cos(pi) == -1), Clause[], funcs=funcs)[1] == true
> @assert resolve(@julog(lookup(foo) == "hello"), Clause[], funcs=funcs)[1] == true
> @assert resolve(@julog(lookup(bar) == "world"), Clause[], funcs=funcs)[1] == true
> 
> ```

More details on usage can be found in the [README](https://github.com/ztangent/Julog.jl), and more examples are in the [test folder](https://github.com/ztangent/Julog.jl/tree/master/test). Let me know if you find this package useful, or have ideas for extensions or improvements! 🙂

(Performance improvements would be especially welcome. I’ve tried all the standard tricks used in Prolog implementations, as well as the standard suggestions for Julia, but the [benchmark tasks](https://github.com/ztangent/Julog.jl/tree/master/benchmarks) still don’t run as fast as in [SWI-Prolog](https://swish.swi-prolog.org/), and it’d be great to find out why! Even if the answer is just that an interpreter is always going to run slower.)

---

<div class="post-metadata">

**Author:** ![gilles-paul](https://avatars.discourse-cdn.com/v4/letter/g/c5a1d2/32.png) [@gilles-paul](https://discourse.julialang.org/u/gilles-paul)\
**Post date:** [May 24, 2021, 9:15am UTC](https://discourse.julialang.org/t/ann-julog-jl-prolog-style-logic-programming-in-julia/35400/2 "2021-05-24T09:15:04Z")

</div>

Hi !  
Thank you for your great work.  
I used to program in Prolog 40 years ago … but I’ve got some souvenirs.  
I’m translating some educational programs from Python to Julia.  
Now I’m busy with some lessons in elementary logic: Propositions of the first order.  
I decided to use Julia+julog to check syntax of expressions.  
I want to begin with ‘atomic’ props such as constants and variables. Connectors and delimiters will be added later.  
Would-be propositions are supposed to be entered as strings using ! for negation, & for conjunction and | for disjonction, together with grouping delimiters, namely parentheses.  
For atomic ones I decided to use only single capital letters.  
The 2 usual constants will be “V” (French ‘vrai’ for true) and “F” (French ‘aux’ for false). All other letters such as A,B,C, X, Y etc… will be considered as variables.  
So I began like this

> using Julog
> 
> varP(f) = length(f) == 1 && f[1] in “ABCDEGHIJKLMNOPQRSTUWXYZ”
> 
> @julog Constante(“V”) \<\<= true # constante ‘vrai’  
> @julog Constante(“F”) \<\<= true # constante ‘faux’  
> @julog Variable(X) \<\<= varP(X)

This compiles without any problem. Ultimately I will add a lot of clauses under the form of rules, but to begin with I would lijke to test if V" and F" are recognized as constants and the other as variables.  
So I wrote println(@julog Constante(“V”)) expecting a ‘true’ answer but no ! it just prints ‘(Constante V)’  
and would print more or less the same I I would ask about K being a constant ???  
I read doc hoping to find the answer (“How to ask if some fact is true ?”) .  
Can you explain and answer.  
Thanks a lot in advance.  
Gilles

---

<div class="post-metadata">

**Author:** ![gilles-paul](https://avatars.discourse-cdn.com/v4/letter/g/c5a1d2/32.png) [@gilles-paul](https://discourse.julialang.org/u/gilles-paul)\
**Post date:** [May 24, 2021, 9:33am UTC](https://discourse.julialang.org/t/ann-julog-jl-prolog-style-logic-programming-in-julia/35400/3 "2021-05-24T09:33:51Z")

</div>

To be more concise my question could be What is the Julog equivalent for Prolog usual query ?-

🙂

---

<div class="post-metadata">

**Author:** ![Xuan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/xuan/32/22100_2.png) [@Xuan](https://discourse.julialang.org/u/Xuan)\
**Post date:** [May 24, 2021, 6:31pm UTC](https://discourse.julialang.org/t/ann-julog-jl-prolog-style-logic-programming-in-julia/35400/4 "2021-05-24T18:31:57Z")

</div>

Glad you’ve decided to check Julog out! To perform a Prolog-style query with respect to a set of clauses, you should use the `resolve` function, i.e., `resolve(query, clauses)`, which will return a tuple of two values, the first being whether the query was resolved/satisfied (which will be `true` or `false`), and the second being a list of variable substitutions that satisfied the query. So in your example, you’ll want write code like:

```julia
clauses = @julog [
    constante(“V”) <<= true,
    constante(“F”) <<= true,
    variable(X) <<= varP(X)
]

query = @julog constante("V")
sat, subst = resolve(query, clauses)

```

And you should find that `sat` is `true`. For a more detailed example, check out the Zen lineage example I used in the original post, and in the [README](https://github.com/ztangent/Julog.jl).

---

<div class="post-metadata">

**Author:** ![gilles-paul](https://avatars.discourse-cdn.com/v4/letter/g/c5a1d2/32.png) [@gilles-paul](https://discourse.julialang.org/u/gilles-paul)\
**Post date:** [May 24, 2021, 11:12pm UTC](https://discourse.julialang.org/t/ann-julog-jl-prolog-style-logic-programming-in-julia/35400/5 "2021-05-24T23:12:53Z")

</div>

Thank you very much for your quick answer. I hope I’ll find some time to test it tomorrow. Till now I have a working functional approach with Julia.

```julia
#les deux constantes Vrai et faux
constP(f) = length(f) == 1 && f[1] in "VF"
#variable, toute autre lettre majuscule que V ou F
varP(f) = length(f) == 1 && f[1] in "ABCDEGHIJKLMNOPQRSTUWXYZ"
#test de parenthèsage
inparP(f) = f[1] == '(' && f[length(f)] == ')'
#ramène l'expression entre parenthèses
betweenpar(f) = f[2:length(f)-1]

function parseP(f) #analyseur syntaxique
    if constP(f) # c'est une constante simple
        return f[1]
    end
    if varP(f) # c'est une variable simple
        return f[1]
    end
    if inparP(f) # l'expression est entre parenthèses'
        a = parseP(betweenpar(f))
        if a != nothing
            return a
        end
    end
    if f[1] == '!' # nous avons une négation
        a = parseP(f[2:end])
        if a != nothing
            return ['!', a]
        end
    end
    if '&' in f # nous avons une conjonction
        for i = 1:length(f)
            if f[i] == '&'
                a = parseP(f[1:i-1])
                b = parseP(f[i+1:end])
                if (a != nothing) && (b != nothing)
                    return ['&', a, b]
                end
            end
        end
    end
    if '|' in f #nous avons une disjonction
        for i = 1:length(f)
            if f[i] == '|'
                a = parseP(f[1:i-1])
                b = parseP(f[i+1:end])
                if (a != nothing) && (b != nothing)
                    return ['|', a, b]
                end
            end
        end
    end
    return nothing
end

```

I think it would be more simple and much more elegant to use inference in a prolog style, so julog is the thing.  
In any case I will keep you informed about the result of my experiments.  
Thank again, regards.  
Gilles

---

<div class="post-metadata">

**Author:** ![gilles-paul](https://avatars.discourse-cdn.com/v4/letter/g/c5a1d2/32.png) [@gilles-paul](https://discourse.julialang.org/u/gilles-paul)\
**Post date:** [May 25, 2021, 4:36am UTC](https://discourse.julialang.org/t/ann-julog-jl-prolog-style-logic-programming-in-julia/35400/6 "2021-05-25T04:36:11Z")

</div>

Good morning !  
I made the test as you suggested it works,  
I can even ask for all constants subst will give [“V”,“F”].  
Well, but …  
To begin with, I must state that simple constants and variables are formulas.  
but the clause identifying variables as we both suggest fails !  
I mean that with this code :

> using Julog
> 
> varP(X) = length(X) == 1 && X[1] in “ABCDEGHIJKLMNOPQRSTUWXYZ”
> 
> clauses = @julog [  
> constante(“V”) \<\<= true,  
> constante(“F”) \<\<= true,  
> variable(X) \<\<= varP(X),  
> formule(X) \<\<= constante(X),  
> formule(X) \<\<= variable(X)  
> ]
> 
> query = @julog variable(“A”)  
> sat, subst = resolve(query, clauses)
> 
> println(sat)  
> println(subst)

I will receive false for sat and empty list for subst, and of course the test of “A” being a formula fails as well.

---

<div class="post-metadata">

**Author:** ![Xuan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/xuan/32/22100_2.png) [@Xuan](https://discourse.julialang.org/u/Xuan)\
**Post date:** [May 25, 2021, 3:33pm UTC](https://discourse.julialang.org/t/ann-julog-jl-prolog-style-logic-programming-in-julia/35400/7 "2021-05-25T15:33:44Z")

</div>

Glad you got the first part working! Because you’re using `varP` as a custom function, you have to tell that to `resolve` when you call it:

```julia
sat, subst = resolve(query, clauses, funcs=Dict(:varP => varP))

```

This is also covered [in this section of the README](https://github.com/ztangent/Julog.jl#custom-functions).

If you have more questions, feel free to open them as a GitHub issue here: [Issues · ztangent/Julog.jl · GitHub](https://github.com/ztangent/Julog.jl/issues)

---

<div class="post-metadata">

**Author:** ![gilles-paul](https://avatars.discourse-cdn.com/v4/letter/g/c5a1d2/32.png) [@gilles-paul](https://discourse.julialang.org/u/gilles-paul)\
**Post date:** [May 25, 2021, 5:51pm UTC](https://discourse.julialang.org/t/ann-julog-jl-prolog-style-logic-programming-in-julia/35400/8 "2021-05-25T17:51:48Z")

</div>

works !  
“A” is recognized as variable and as formula as well.  
Thanks again for your quick and efficient help. OK ! now parse as formulas constants and variables, which is a good beginning waiting for more. 😉  
BTW I did it under Python using the lex Yacc (PLY) library. Trying to imitate, I couldn’t install the equivalent for Julia I will retry later, this is not the subject here.  
I will continue tomorrow with new rules.  
The context-free grammar describing formulas in logic of the first order is quite simple. I should complete more or less quickly.

---

<div class="post-metadata">

**Author:** ![gilles-paul](https://avatars.discourse-cdn.com/v4/letter/g/c5a1d2/32.png) [@gilles-paul](https://discourse.julialang.org/u/gilles-paul)\
**Post date:** [May 27, 2021, 7:52pm UTC](https://discourse.julialang.org/t/ann-julog-jl-prolog-style-logic-programming-in-julia/35400/9 "2021-05-27T19:52:06Z")

</div>

I want to restart everything all over from scratch !  
OK I tried

```julia
using Julog
clauses = @julog [
    constante(V) <<= true,
    constante(F) <<= true
 ]
#println(conj("A&B","A","B"))
query = @julog constante(X)
sat, subst = resolve(query, clauses )
println(sat)
println(subst)

```

Answer

```julia
true
Any[{X => #2}, {X => #3}]

```

what do these #2 and #3 stand for ?  
I’m completely lost !

---

<div class="post-metadata">

**Author:** ![gilles-paul](https://avatars.discourse-cdn.com/v4/letter/g/c5a1d2/32.png) [@gilles-paul](https://discourse.julialang.org/u/gilles-paul)\
**Post date:** [May 27, 2021, 7:58pm UTC](https://discourse.julialang.org/t/ann-julog-jl-prolog-style-logic-programming-in-julia/35400/10 "2021-05-27T19:58:39Z")

</div>

Sorry the above post is in the wrong place, you can delete it.

---

<div class="post-metadata">

**Author:** ![Neurosurg](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/neurosurg/32/26324_2.png) [@Neurosurg](https://discourse.julialang.org/u/Neurosurg)\
**Post date:** [November 9, 2021, 3:15pm UTC](https://discourse.julialang.org/t/ann-julog-jl-prolog-style-logic-programming-in-julia/35400/11 "2021-11-09T15:15:01Z")

</div>

could any one come up with a simple example for subjective logic based on Julog.jl, please?

---

<div class="post-metadata">

**Author:** ![Xuan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/xuan/32/22100_2.png) [@Xuan](https://discourse.julialang.org/u/Xuan)\
**Post date:** [November 10, 2021, 10:24am UTC](https://discourse.julialang.org/t/ann-julog-jl-prolog-style-logic-programming-in-julia/35400/12 "2021-11-10T10:24:58Z")

</div>

If by subjective logic you mean [this](https://en.wikipedia.org/wiki/Subjective_logic), unfortunately Julog.jl doesn’t support reasoning about uncertain propositions or any kind of probabilistic reasoning. You might be interested in [Problox.jl](https://github.com/femtomc/Problox.jl), a Julia wrapper for ProbLog. You could possibly also extend Julog.jl to support probabilistic logic in the style of ProbLog, and maybe even get subjective logic style reasoning by extending ProbLog with Beta-distributed priors ([as in this paper](https://lirias.kuleuven.be/retrieve/592231)).

---

<div class="post-metadata">

**Author:** ![slwu89](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/slwu89/32/217323_2.png) [@slwu89](https://discourse.julialang.org/u/slwu89)\
**Post date:** [December 19, 2024, 5:23pm UTC](https://discourse.julialang.org/t/ann-julog-jl-prolog-style-logic-programming-in-julia/35400/13 "2024-12-19T17:23:12Z")

</div>

I just discovered this package while looking for anything Prolog-like in Julia, after becoming interested in logic programming from reading Sowa’s Knowledge Representation book. It looks really nice. I’m curious to ask the author, what was the original intention behind creating it? Is it being used as a backend or logic engine for any other projects? What’s the future of the package?

---

<div class="post-metadata">

**Author:** ![Xuan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/xuan/32/22100_2.png) [@Xuan](https://discourse.julialang.org/u/Xuan)\
**Post date:** [December 19, 2024, 6:35pm UTC](https://discourse.julialang.org/t/ann-julog-jl-prolog-style-logic-programming-in-julia/35400/14 "2024-12-19T18:35:26Z")

</div>

Thanks!

It’s currently used as a backend solver for the [PDDL.jl](https://github.com/JuliaPlanners/PDDL.jl) planning library which I also develop and maintain. I don’t have big plans for future work on Julog.jl itself, but very happy to accept PRs etc. that would improve performance.
