Clojure core.logic CLP(FD) projecting FD variables

2019-02-27 03:36发布

问题:

I'm working on a naive square-packing algorithm using Clojure's core.logic CLP(FD) library (core.logic version 0.8.3).

Squares are represented like so:

[[[x11 y11] [x12 y12]] 
 [[x21 y21] [x22 y22] ...]]

with each square represented as the coordinates of its top-left and bottom-right corners.

Coordinates are FD variables, within a certain interval.

I want to define the size of a solution as the distance between the top-right corner and bottom-right corners of the closest and farthest squares to the origin, respectively

(defne solution-size-o [size squares]
  ([s sqrs]
    (fresh [closest farthest 
            x11 y11 x22 y22 _1 _2]
    (closest-square   [[x11 y11] _1] sqrs)
    (farthest-square  [_2 [x22 y22]] sqrs)
    (project [x11 y11 x22 y22]
      (let [a (- y22 y11)
            b (- x22 x11)]
        (== s (-> (+ (* a a) (* b b)) Math/sqrt Math/ceil int)))))))

This seems to work fine with plain integers:

(run 1 [q]
  (solution-size-o q [[[0 0] [1 1]] [[1 1] [2 2]]]))
=> (3)

And even with fully-constrained FD variables

(defn constrained-solution-size []
  (run 1 [q] 
    (fresh [size x11 y11 
                 x12 y12 
                 x21 y21 
                 x22 y22 squares]
      (fd/in x11 y11 x12 y12 x21 y21 x22 y22 (fd/interval 0 2))
      (fd/eq 
        (= x11 0) (= y11 0) (= x21 1) (= y21 1)
        (= x12 (+ x11 1)) (= y12 (+ y11 1))
        (= x22 (+ x21 1)) (= y22 (+ y21 1)))
      (== squares [[[x11 y11] [x12 y12]] [[x21 y21] [x22 y22]]])
      (solution-size-o size squares)
      (== q {:squares squares :size size}))))

(constrained-solution-size)
=> ({:squares [[[0 0] [1 1]] [[1 1] [2 2]]], :size 3})

But it seems to break when the domains of the variables isn't fully constrained. For example, if I remove the constraint that y21 = 1, meaning y11 and y21 have more than one value left in their domains:

(defn unconstrained-solution-size []
  (run 1 [q] 
    (fresh [size x11 y11 
                 x12 y12 
                 x21 y21 
                 x22 y22 squares]
      (fd/in x11 y11 x12 y12 x21 y21 x22 y22 (fd/interval 0 2))
      (fd/eq 
        (= x11 0) (= y11 0) (= x21 1)
        (= x12 (+ x11 1)) (= y12 (+ y11 1))
        (= x22 (+ x21 1)) (= y22 (+ y21 1)))
      (== squares [[[x11 y11] [x12 y12]] [[x21 y21] [x22 y22]]])
      (solution-size-o size squares)
      (== q {:squares squares :size size}))))

I get

(unconstrained-solution-size)
=> ClassCastException clojure.core.logic.LVar cannot be cast to java.lang.Number     clojure.lang.Numbers.minus (Numbers.java:135)

It seems that project only works on FD variables when their domains are fully constrained. Is this how it should be? If it is, does anyone have any suggestions on how to carry out non-relational arithmetic on FD variables?

Thanks!

回答1:

Yes you cannot project finite domain vars that haven't been constrained to single value. I recommend looking at existing solutions to your problem in Prolog that leverage CLP(FD). It may very well be that we don't support enough constraints to make this problem simple to express - we're working on that.