core-logic

Quentin Le Guennec 2021-12-13T20:41:55.080500Z

I'm getting unexpected results from a core.logic expression:

(let [input  (mapv (partial mapv #(lvar % false))
                   '((0 1)
                     (0 1 2 3 4 5 6)
                     (0 1 2 3 4 5)
                     (0 1 2 3 4 5)
                     (0 1 2 3)
                     (0 1 2 3 4)
                     (0 1 2 3 4 5)
                     (0 1 2 3 4)
                     (0 1 2 3 4)
                     (0 1 2)))
      model [[:a :b :c :e :f :g]
             [:c :f]
             [:a :c :d :e :g]
             [:a :c :d :f :g]
             [:b :c :d :f]
             [:a :b :d :f :g]
             [:a :b :d :e :f :g]
             [:a :c :f]
             [:a :b :c :d :e :f :g]
             [:a :b :c :d :f :g]]]
  (->> (run* [q]
         (everyg (fn [s] (membero s model)) input)
         (== q input)) ))
I would like to solve: "For all values from 0 to 9 in input , what is the corresponding value from :ato :f ?" (e.g: (0 1) can only map to [:c :f] because it is the only length 2 collection.) Is that what I'm expressing as goals in run*? Or am I expressing something else?

2021-12-13T21:58:23.081700Z

its not a great problem for logic/relational programming, things defined in terms of counting are tricky

Quentin Le Guennec 2021-12-13T21:59:24.082100Z

Are you sure? I think I'm pretty much using logic programming when I'm trying to solve it myself

2021-12-13T21:59:37.082400Z

yes

Quentin Le Guennec 2021-12-13T21:59:53.082900Z

what do you mean by counting exactly though?

2021-12-13T22:00:44.083200Z

you are defining the correspondence relation based on the count (the size, the length) of collections

Quentin Le Guennec 2021-12-13T22:01:01.083400Z

Oh yeah

Quentin Le Guennec 2021-12-13T22:01:40.084300Z

Isn't it supposed to work with permuteo though? I updated it:

(run* [q]
         (everyg (fn [s]
                   (or*
                    (map (partial permuteo s)
                         model)))
                 input)
         (== q input))
But still unsuccessful

Quentin Le Guennec 2021-12-13T22:02:00.084800Z

I guess I'll try something else

2021-12-13T21:59:25.082300Z

if you define it in some way that isn't based on counting it will go much better

2021-12-13T21:59:56.083100Z

like say, all rows are the same length, but have some pattern of true, false

2021-12-13T22:02:45.085700Z

so if you replace all you vectors of letters with things like [false false true false false true false] which would correspond to [:c :f] because those positions are true

Quentin Le Guennec 2021-12-13T22:03:26.086Z

I see

2021-12-13T22:03:34.086200Z

input in your example is just kind of bogus

2021-12-13T22:03:52.086600Z

I am not sure even what you are trying to do with it

2021-12-13T22:04:16.087400Z

there are reasons to call the lvar constructor with arguments, but basically you never need to

Quentin Le Guennec 2021-12-13T22:05:42.088800Z

Well [0 1] => [:c :f], [0 1 2 3] => [:b :c :d :f] (permutable), because they uniquely got the same length. The rest should follow with the the numbers mapped to the letters.

Quentin Le Guennec 2021-12-13T22:06:09.089300Z

Because 0 should be the same across all the input

Quentin Le Guennec 2021-12-13T22:06:41.089800Z

0 in the first element of input should be the same than in the second

Quentin Le Guennec 2021-12-13T22:09:52.091800Z

So from [0 1] => `[:c :f]` , we can deduce that (or (and 0 => c, 1=> f) (and 0 => f, 1 => c)

2021-12-13T22:09:58.092Z

thats not how it works though, like, you are saying the vars are members of the lists, 2 vars can be members of 3 element list just as easy as they can be members of a 2 element list

2021-12-13T22:10:08.092300Z

and you have no kind of unique constraints

2021-12-13T22:11:09.093Z

so 2 vars can be members of a 1 element list easily as well

Quentin Le Guennec 2021-12-13T22:11:32.093200Z

I'm not sure I understand

2021-12-13T22:11:46.093500Z

if X and Y are members of L

2021-12-13T22:12:05.094Z

if L is just (1) then X a and Y, absent other constraints, are both 1

Quentin Le Guennec 2021-12-13T22:12:35.094200Z

Oh, that makes sense.

Quentin Le Guennec 2021-12-13T22:14:42.094800Z

I think that's why I'm getting some kind of factorial of all possibilities as a result

2021-12-13T22:16:23.095800Z

but even then, if you can, it is best to avoid membero, because membero is at best a linear search in a list, so combinatorial is bad

Quentin Le Guennec 2021-12-13T22:17:54.096200Z

I think that linear search is pretty much required by the problem itself

2021-12-13T22:18:57.096800Z

maybe, hard to tell from the code and description given

2021-12-13T22:19:50.097600Z

there is search through the space of all possibilities, then there is search through data structures for the right things to unify

Quentin Le Guennec 2021-12-13T22:20:10.097800Z

I see.

2021-12-13T22:25:41.099500Z

like, the code you shared has no branching of the search tree, other then the recursive cases for membero

2021-12-13T22:26:34.100500Z

so outside the is this lvar the first, the second, the third, ..., etc member of this list, there are no branches

2021-12-13T22:27:02.101100Z

so its not like it is considering all the model or inputs as different searches or something

Quentin Le Guennec 2021-12-13T22:32:18.101900Z

Do you think I should give up on core.logic? It's an advent of code exercise and I thought it was a nice way to learn core.logic

2021-12-13T22:34:47.103300Z

I dunno, there is definitely a significant hump to get over

2021-12-13T22:35:36.104400Z

core.logic is kind of a bare bones logic programming environment, so figuring out how to represent problems in a way that works can be tricky

Quentin Le Guennec 2021-12-13T22:36:14.105100Z

I see

2021-12-13T22:37:04.106Z

and because core.logic is a dsl embedded in clojure, often the way to do things is with clojure as a meta/macro language for constructing core.logic programs, which is also tricky to get the hang of sometimes (see all the troubles people get into learning macros)

2021-12-13T22:39:10.107600Z

my understanding of logic programming comes in large part from sitting down with the minikanren paper and implementing it in go, and then writing solvers for a bunch of logic riddles in it as tests, not from working with core.logic a lot