solving an equation for all solutions with constraints: core.logic.fd

286 views
Skip to first unread message

cej38

unread,
Jun 10, 2014, 11:12:11 AM6/10/14
to clo...@googlegroups.com
I am interested in solving a simple equation for all of its solutions when some constraints are applied.  This sounds like a problem for core.logic.fd.

Let's use a toy example:

8 = 3*x + 2*y,  where x and y must be non-negative integers.

Here are the possible solutions:  [x,y]= {[2,1],[0,4]}.


I tried something like this:

(run* [q]
      (fresh [x y]
             (== q [x y])
                   (project [x y]
      (fd/+ (* x 2) (y 3) 8))))

But I get a exception:
"java.lang.ClassCastException: clojure.core.logic.LVar cannot be cast to java.lang.Number"

So I have two questions:
1. How should I rewrite the above command to get the solutions?
2. Are there any good blog posts/online presentations/etc. on core.logic.fd?



cej38

unread,
Jun 10, 2014, 11:28:46 AM6/10/14
to clo...@googlegroups.com
I found the solution to my first problem at https://github.com/clojure/core.logic/wiki/Features (with a few small changes by me):

(run* [q]
  (fresh [x y]
    (fd/in x y (fd/interval 0 9))
    (fd/eq
      (= (+ (* x 3) (* y 2)) 8))
    (== q [x y])))


I suppose that I could set (fd/interval 0 999999999) to do a fair approximation of the non-negative integers, but out of curiosity, would there be a better way of doing this?

David Nolen

unread,
Jun 10, 2014, 11:32:33 AM6/10/14
to clojure
That's that's the suggested way - just pick a large bound.

David
> --
> You received this message because you are subscribed to the Google
> Groups "Clojure" group.
> To post to this group, send email to clo...@googlegroups.com
> Note that posts from new members are moderated - please be patient with your
> first post.
> To unsubscribe from this group, send email to
> clojure+u...@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/group/clojure?hl=en
> ---
> You received this message because you are subscribed to the Google Groups
> "Clojure" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to clojure+u...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

Alex Engelberg

unread,
Jun 10, 2014, 2:11:29 PM6/10/14
to clo...@googlegroups.com
I couldn't help but overhear that you're working with Constraint Programming, so I thought I'd suggest that you try loco, which is a Clojure wrapper of a Java library specifically designed to work with these types of problems.

The loco solution to your problem would look like this:
(use 'loco.core 'loco.constraints)
(solutions [($in :x 0 100)
            ($in :y 0 100)
            ($= ($+ ($* :x 2)
                    ($* :y 3))
                8)])
=> ({:y 0, :x 4} {:y 2, :x 1})
While loco doesn't free you from all the limitations you previously had (like having to specify arbitrarily large domains), it might be a better choice than core.logic in this case because I think it's more elegant to write problems, and it's much faster. (I wrote the library so I'm a bit biased.)

cej38

unread,
Jun 10, 2014, 5:30:20 PM6/10/14
to clo...@googlegroups.com
I picked a toy problem that was really easy to solve, figuring that once I had the idea down, I would be able to easily change the equation to the one that I am interested in solving.  In moving to my real problem I hit the next snag I can't use real numbers within the equation.  I note that the equation hasn't changed, the values of x and y are still non-negative integers.

(run* [q]
  (fresh [x y]
    (fd/in x y (fd/interval 0 9))
    (fd/eq
      (= (+ (* x 3.) (* y 2.)) 8.))
    (== q [x y])))

In my real problem I expect that I could have something like 

(run* [q]
  (fresh [x y]
    (fd/in x y (fd/interval 0 9))
    (fd/eq
      (= (+ (* x 3.1111) (* y 2.2222)) 8.4444))
    (== q [x y])))

Is there away to do this?

Alex Engelberg

unread,
Jun 10, 2014, 8:28:18 PM6/10/14
to clo...@googlegroups.com
It doesn't totally make sense to me that you would have integer variables with real coefficients. If the coefficients are irrational and are not scaled versions of each other, then the problem is impossible. Otherwise, you can just scale them by a common factor and make them integers. For instance, you could turn 3.1111x + 2.2222y = 8.44444 into 28x + 20y = 76.

--Alex


--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to a topic in the Google Groups "Clojure" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/clojure/hje351kbvJA/unsubscribe.
To unsubscribe from this group and all its topics, send an email to clojure+u...@googlegroups.com.

David Nolen

unread,
Jun 11, 2014, 9:28:29 AM6/11/14
to clojure
core.logic only currently supports solving finite domains CLP(FD),
you'll have to look elsewhere for a CLP(R) solver for now.

David
Reply all
Reply to author
Forward
0 new messages