Hi Will!
The `split` relation in the code you linked (llmr.scm) is a very similar
relation on the domain of multisets. Thank you for sharing!
Regarding another use of `riffleo`: I have been trying to transform an
undirected graph into a sequence of nonoverlapping bicliques (complete
bipartite subgraphs). This is always possible, since every graph has a
maximum independent edge set (which can be thought of as (1, 1)-bicliques).
This decomposition into bicliques has an application in graph coloring.
I have found `riffleo` useful for partitioning the vertices of the graph
into 3 sets: `left`, `right`, and `rest-v`. Then I can check if there is an
edge from every vertex in `left` to every vertex in `right`. Here is the
code for that. Hopefully the implicit relations make sense.
(defrel (bicliqueso graph bicliques)
(conde
((== bicliques '()) (empty-grapho graph))
((fresh (v e rest-v rest-e rest-g rest-bicliques left right*rest-v
right)
;; A biclique is a pair of vertex sets
(== bicliques `((,left . ,right) . ,rest-bicliques))
(pairo left)
(pairo right)
;; Assume an efficient undirected graph data structure
(verticeso g v)
(verticeso rest-g rest-v)
(edgeso g e)
(edgeso rest-g rest-e)
* ;; partition the vertices into nonempty `left`, nonempty `right`,
and `rest-v`*
* (riffleo left right*rest-v v) (riffleo right rest-v
right*rest-v)*
;; check that every vertex in `left` is connected to every vertex in
`right`
(is-bicliqueo left right g)
;; Keep only the edges which have both vertices in `rest-v`
(filtero e rest-v rest-e)
(bicliqueso rest-g rest-bicliques)))))
This ended up being unfeasible since I could not think of a good data
structure for representing an undirected graph in miniKanren, and I found
it difficult to write `is-bicliqueo`. I ended up porting `riffleo` into
Python as `unriffle`.
def unriffle(o: List[T]) -> Iterator[Tuple[List[T], List[T]]:
...
I used that for a while in Python until I figured out a bottom-up way to
find all bicliques by constructing a table all_bicliques_of_size[m][n].
TL;DR - `riffleo` can be used to partition a set into n disjoint subsets,
which may be useful for detecting bicliques.
Brett
On Monday, October 24, 2022 at 1:16:10 PM UTC-4 William Byrd wrote:
> Hi Brett!
>
> I don't recall if I've used `riffleo` before, but I agree that it looks
> useful!
>
> We may have used something similar in this code:
>
> https://github.com/webyrd/linear-logic-multiset-rewriting
>
> What are other examples of uses of `riffleo` that you have encountered?
>
> Cheers,
>
> --Will
>
> On Mon, Oct 24, 2022 at 12:35 PM Brett Schreiber <[email protected]>
> wrote:
>
>> 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
>>
>> <https://groups.google.com/d/msgid/minikanren/ac5e6700-f7c6-4e1c-8915-d9db0907c715n%40googlegroups.com?utm_medium=email&utm_source=footer>
>> .
>>
>
--
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/b916b9a1-d5e9-4627-bd1c-d94d25713ed7n%40googlegroups.com.