Hello all,
I have become very interested in relational programming, and I have enjoyed 
trying to a variety of search problems in miniKanren. I have found a 
relation that has been useful to many of these problems.

Consider the relation a * b = o where:

   1. a is a list
   2. b is another list, and
   3. o is the result of nondeterministically merging the lists, i.e., (a * 
   b)

It reminds me of the "riffle" step when shuffling a deck of cards.
Here is an example:

a = '(    a   b c     d   e     f)
b = '(1 2   3     4 5   6   7 8  )
o = '(1 2 a 3 b c 4 5 d 6 e 7 8 f)

Some properties:

   - a * (b * c) = (a * b) * c
   - |a * b| = |a| + |b|
   - (a * b) contains a and b as subsequences
      - which implies if (a * b) is sorted, then a is sorted and b is sorted
   - a * b = b * a

Notice that appendo shares all of these properties except the last one. So 
this is some sort of generalization of appendo.

Here is the relation's definition in miniKanren, where a * b = o is written 
(riffleo 
a b o)

(defrel (riffleo a b o)
    (fresh (car-a cdr-a car-b cdr-b car-o cdr-o)
        (conde
            ;; If `a` and `b` are both empty, then the output is empty.
            ((== a '()) (== b '()) (== o '()))
            
            ;; If `a` is non-empty and `b` is empty,
            ;; then the output is equal to `a`.
            ((== a `(,car-a . ,cdr-a)) (== b '()) (== o a))
            
            ;; If `a` is empty and `b` is non-empty,
            ;; then the output is equal to `b`.
            ((== a '()) (== b `(,car-b . ,cdr-b)) (== o b))
            
            ;; When both `a` and `b` are non-empty
            ((== a `(,car-a . ,cdr-a)) (== b `(,car-b . ,cdr-b)) (== o 
`(,car-o . ,cdr-o))
                (conde
                    ((== car-o car-a) (riffleo cdr-a b cdr-o))
                    ((== car-o car-b) (riffleo a cdr-b cdr-o)))))))
                    
And after applying a correctness-preserving transformation:

(defrel (riffleo a b o)
    (fresh (car-a cdr-a car-b cdr-b car-o cdr-o *z0 z1*)
        (conde
            ;; If `a` and `b` are both empty,
            ;; then the output is empty.
            ((== a '()) (== b '()) (== o '()))
            
            ;; If `a` is non-empty and `b` is empty,
            ;; then the output is equal to `a`.
            ((== a `(,car-a . ,cdr-a)) (== b '()) (== o a))
            
            ;; If `a` is empty and `b` is non-empty, then the output is 
equal to `b`.
            ((== a '()) (== b `(,car-b . ,cdr-b)) (== o b))
            
            ;; When both `a` and `b` are non-empty
            ((== a `(,car-a . ,cdr-a)) (== b `(,car-b . ,cdr-b)) (== o 
`(,car-o . ,cdr-o))
                



*(conde                    ((== car-o car-a) (== z0 cdr-a) (== z1 b))      
              ((== car-o car-b) (== z0 a) (== z1 cdr-b)))                  
                  (riffleo z0 z1 cdr-o)*))))

I have found riffleo to be useful as a "choose" function when the arguments 
are considered multisets rather than lists. For example, it makes it easy 
to write the NP-complete 3-partition problem 
<https://en.wikipedia.org/wiki/3-partition_problem> as a relation.

(defrel (three-partitiono l partitions sum)
    (fresh (e0 e1 e2 rest-l e0+e1 rest-partitions)
        (conde
            ;; Base case
            ((== l '())
                (== partitions '())))

            ;; Recursive case
            ((== partitions `((,e0 ,e1 ,e2) . ,rest-partitions))
                (riffleo `(,e0 ,e1 ,e2) rest-l l)
                (+o e0 e1 e0+e1)
                (+o e0+e1 e2 sum)
                (three-partitiono rest-l rest-partitions sum))))


Has anyone else encountered this riffle relation?

Best,
Brett Schreiber

-- 
You received this message because you are subscribed to the Google Groups 
"minikanren" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/minikanren/ac5e6700-f7c6-4e1c-8915-d9db0907c715n%40googlegroups.com.

Reply via email to