tgk / propaganda

A propagator library for Clojure

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Is it possible to define predicate type propagators?

muhuk opened this issue · comments

For instance, is it possible to model the relationships between the edge lengths of a triangle? As in:

| b - c | <= a <= b + c
| a - c | <= b <= a + c
| a - b | <= c <= a + b

I know I can create an intermediary (two in the <= case) cell, but I couldn't find a way to have a propagator result in an interval.

I have tried the following:

(def gte (function->propagator-constructor >=))
(def lte (function->propagator-constructor <=))


(defn between [lo v hi]
  (let [k (make-cell)
        l (make-cell)
        m (make-cell)
        n (make-cell)]
    (lte lo v k)
    (lte v hi l)
    (gte v lo m)
    (gte hi v n)
    (add-value k true)
    (add-value l true)
    (add-value m true)
    (add-value n true)))


(binding [*merge* (default-merge)]
  (let [a (make-cell)
        b (make-cell)
        c (make-cell)]
    (between a b c)
    (add-value a 5)
    (add-value c 9)
    (get-value b)))

Evaluating the last expression gives: :propaganda.values/nothing

(Expected result would be [5 9] of course)

This should illustrate what I am trying to do better:

(def >=p
  (-> (fn [x] (make-interval x Double/POSITIVE_INFINITY))
      (function->propagator-constructor)))

(def <=p
  (-> (fn [x] (make-interval Double/NEGATIVE_INFINITY x))
      (function->propagator-constructor)))

(defn gtep [a b]
  (>=p a b)
  (<=p b a))

(defn between [lo v hi]
  (gtep v lo)
  (gtep hi v))


(binding [*merge* (extend-merge (default-merge))]
  (let [a (make-cell)
        b (make-cell)
        c (make-cell)]
    (between a b c)
    (add-value a 5.0)
    (add-value c 3.0)
    (get-value b)))

This time I get an exception; propaganda.intervals.common.Interval cannot be cast to java.lang.Number

This is from the stacktrace of the error above:

              Numbers.java:  225  clojure.lang.Numbers/lte
                  core.clj: 1045  clojure.core/<=
                  core.clj: 1036  clojure.core/<=
               RestFn.java:  442  clojure.lang.RestFn/invoke
                common.clj:   57  propaganda.intervals.common/ensure-inside
                common.clj:   53  propaganda.intervals.common/ensure-inside
                common.clj:  142  propaganda.intervals.common/extend-merge/fn
                  AFn.java:  156  clojure.lang.AFn/applyToHelper
                  AFn.java:  144  clojure.lang.AFn/applyTo
                  core.clj:  646  clojure.core/apply
                  core.clj:  641  clojure.core/apply

It seems (and I might be wrong) extend-merge matches number? interval? (or the other way around) instead of interval? interval?. Could this be a bug?

Thanks for the very interesting question! I haven't touched this in a while but let me see if I can help anyway.

In general it is not possible to go from a predicate back to a value. I'm pretty sure that if it was we would have solved the halting problem so we can't go that way.

The functions for making intervalse won't get you there either as they would just return simple interval values and what you want is a two-way propagation of values.

I think you might have to implement both sides of the bijective mapping you are interested in that represents:

All x, y, b such that b is true when x <= y.

Something tells me you can't use the existing easy-to-use wrappers for this as they normally only deal with one input and one output. You'll have to combine multiple of them to get there I'm afraid. Something along the lines of:

  • one for (x, y) => b (then one you already have)
  • one for (x, b) => y (which will return an interval)
  • one for (y, b) => x (which will return an interval)

If you combine these three I think you should be where you want for the simple relation. You can then combine two of those to get to (<= x y z).

    Something tells me you can't use the existing easy-to-use wrappers for this as they normally only deal with one input and one output. You'll have to combine multiple of them to get there I'm afraid. Something along the lines of:

    - one for (x, y) => b (then one you already have)
    - one for (x, b) => y (which will return an interval)
    - one for (y, b) => x (which will return an interval)

This is specifically and precisely what I am trying to achieve. But I must say it doesn't really help at the moment. I am reading it as another statement of the problem, not as hints to the solution. (Probably because I don't get the hint here)

The functions for making intervalse won't get you there either as they would just return simple interval values and what you want is a two-way propagation of values.

I was thinking >=p & <=p in gtep context would give me two way propagation. I read it as;

  • Set two propagators.
  • If we know a, merge the value of b with the interval that's the result of >=p.
  • If we know b, merge the value of a with the interval that's the result of <=p.

Do I need to add propagators that handle interval inputs as well? As in: If we know a is this interval....

I see. >=p and <=p are not quite there yet I guess. I don't have a working solution, but it should be possible to get there.

I think it's important to realise that the intervals that are used for the internal representation might not be the same type of intervals you are interested in returning. I think you're trying to work on two different levels at once. For example, could you have a relation such that

a <= b <= c

But a is in [-10, -5] and b is in [-7, 7] so the statement didn't always hold true - it would have to be locked by a commitment of a to a single number. What I'm trying to say is that intervals are a representation of the result values, not a relation in themselves.

I'm afraid I would have to have a bit more of a go at implementing what you are trying to do to give any more meaningful insight than that.

What I'm trying to say is that intervals are a representation of the result values

I was afraid of this.

Makes sense. Thanks for the looking into it.

I'm closing this now, as it was more of a question than a real issue.

No problem! Best of luck with your project. I highly recommend the original article The Art of the Propagator for more inspiration.