Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lazy Folding of Infinite Depth & Infinite Breadth Rose Tree to its Edge Paths

This question haskell fold rose tree paths delved into the code for folding a rose tree to its paths. I was experimenting with infinite rose trees, and I found that the provided solution was not lazy enough to work on infinite rose trees with infinity in both depth and breadth.

Consider a rose tree like:

data Rose a = Rose a [Rose a] deriving (Show, Functor)

Here's a finite rose tree:

finiteTree = Rose "root" [ 
    Rose "a" [ 
        Rose "d" [], 
        Rose "e" []
    ], 
    Rose "b" [ 
        Rose "f" [] 
    ], 
    Rose "c" [] 
]

The output of the edge path list should be:

[["root","a","d"],["root","a","e"],["root","b","f"],["root","c"]]

Here is an infinite Rose tree in both dimensions:

infiniteRoseTree :: [[a]] -> Rose a
infiniteRoseTree ((root:_):breadthGens) = Rose root (infiniteRoseForest breadthGens)

infiniteRoseForest :: [[a]] -> [Rose a]
infiniteRoseForest (breadthGen:breadthGens) = [ Rose x (infiniteRoseForest breadthGens) | x <- breadthGen ]

infiniteTree = infiniteRoseTree depthIndexedBreadths where
    depthIndexedBreadths = iterate (map (+1)) [0..]

The tree looks like this (it's just an excerpt, there's infinite depth and infinite breadth):

      0
      |
      |
    [1,2..]
    /  \
   /    \
  /      \
[2,3..]  [2,3..]

The paths would look like:

[[0,1,2..]..[0,2,2..]..] 

Here was my latest attempt (doing it on GHCi causes an infinite loop, no streaming output):

rosePathsLazy (Rose x []) = [[x]]
rosePathsLazy (Rose x children) = 
    concat [ map (x:) (rosePathsLazy child) | child <- children ]

rosePathsLazy infiniteTree

The provided solution in the other answer also did not produce any output:

foldRose f z (Rose x []) = [f x z]
foldRose f z (Rose x ns) = [f x y | n <- ns, y <- foldRose f z n]

foldRose (:) [] infiniteTree

Both of the above work for the finite rose tree.

I tried a number of variations, but I can't figure out to make the edge folding operation lazy for infinite 2-dimensional rose tree. I feel like it has something to do with infinite amounts of concat.

Since the output is a 2 dimensional list. I can run a 2 dimensional take and project with a depth-limit or a breadth-limit or both at the same time!

Any help is appreciated!


After reviewing the answers here and thinking about it a bit more. I came to the realisation that this is unfoldable, because the resulting list is uncountably infinite. This is because an infinite depth & breadth rose tree is not a 2 dimensional data structure, but an infinite dimensional data structure. Each depth level confers an extra dimension. In other words, it is somewhat equivalent to an infinite dimensional matrix, imagine a matrix where each field is another matrix.. ad-infinitum. The cardinality of the infinite matrix is infinity ^ infinity, which has been proven (I think) to be uncountably infinite. This means any infinite dimensional data structure is not really computable in a useful sense.

To apply this to the rose tree, if we have infinite depth, then the paths never enumerate past the far left of the rose tree. That is this tree:

      0
      |
      |
    [1,2..]
    /  \
   /    \
  /      \
[2,3..]  [2,3..]

Would produce a path like: [[0,1,2..], [0,1,2..], [0,1,2..]..], and we'd never get past [0,1,2..].

Or in another way, if we have a list containing lists ad-infinitum. We can also never count (enumerate) it either, as there would be an infinite amount of dimensions that the code would jump to.

This also has some relationship to real numbers being uncountably infinite too. In a lazy list of infinite real numbers would just infinitely produce 0.000.. and never enumerate past that.

I'm not sure how to formalise the above explanation, but that's my intuition. (For reference see: https://en.wikipedia.org/wiki/Uncountable_set) It'd be cool to see someone expand on applying https://en.wikipedia.org/wiki/Cantor's_diagonal_argument to this problem.

This book seems to expand on it: https://books.google.com.au/books?id=OPFoJZeI8MEC&pg=PA140&lpg=PA140&dq=haskell+uncountably+infinite&source=bl&ots=Z5hM-mFT6A&sig=ovzWV3AEO16M4scVPCDD-gyFgII&hl=en&sa=X&redir_esc=y#v=onepage&q=haskell%20uncountably%20infinite&f=false

like image 227
CMCDragonkai Avatar asked Oct 14 '15 14:10

CMCDragonkai


3 Answers

For some reason, dfeuer has deleted his answer, which included a very nice insight and only a minor, easily-fixed problem. Below I discuss his nice insight, and fix the easily-fixed problem.

His insight is that the reason the original code hangs is because it is not obvious to concat that any of the elements of its argument list are non-empty. Since we can prove this (outside of Haskell, with paper and pencil), we can cheat just a little bit to convince the compiler that it's so.

Unfortunately, concat isn't quite good enough: if you give concat a list like [[1..], foo], it will never draw elements from foo. The universe collection of packages can help here with its diagonal function, which does draw elements from all sublists.

Together, these two insights lead to the following code:

import Data.Tree
import Data.Universe.Helpers

paths (Node x []) = [[x]]
paths (Node x children) = map (x:) (p:ps) where
    p:ps = diagonal (map paths children)

If we define a particular infinite tree:

infTree x = Node x [infTree (x+i) | i <- [1..]]

We can look at how it behaves in ghci:

> let v = paths (infTree 0)
> take 5 (head v)
[0,1,2,3,4]
> take 5 (map head v)
[0,0,0,0,0]

Looks pretty good! Of course, as observed by ErikR, we cannot have all paths in here. However, given any finite prefix p of an infinite path through t, there is a finite index in paths t whose element starts with prefix p.

like image 170
Daniel Wagner Avatar answered Oct 13 '22 12:10

Daniel Wagner


Not a complete answer, but you might be interested in this detailed answer on how Haskell's permutations function is written so that it works on infinite lists:

What does this list permutations implementation in Haskell exactly do?

Update

Here's a simpler way to create an infinite Rose tree:

iRose x = Rose x [ iRose (x+i) | i <- [1..] ]

rindex (Rose a rs) [] = a
rindex (Rose _ rs) (x:xs) = rindex (rs !! x) xs

Examples:

rindex (iRose 0) [0,1,2,3,4,5,6]     -- returns: 26
rindex infiniteTree [0,1,2,3,4,5,6]  -- returns: 13

Infinite Depth

If a Rose tree has infinite depth and non-trivial width (> 1) there can't be an algorithm to list all of the paths just using a counting argument - the number of total paths is uncountable.

Finite Depth & Infinite Breadth

If the Rose tree has finite depth the number of paths is countable even if the trees have infinite breadth, and there is an algorithm which can produce all possible paths. Watch this space for updates.

like image 31
ErikR Avatar answered Oct 13 '22 13:10

ErikR


ErikR has explained why you can't produce a list that necessarily contains all the paths, but it is possible to list paths lazily from the left. The simplest trick, albeit a dirty one, is to recognize that the result is never empty and force that fact on Haskell.

paths (Rose x []) = [[x]]
paths (Rose x children) = map (x :) (a : as)
  where
    a : as = concatMap paths children
    -- Note that we know here that children is non-empty, and therefore
    -- the result will not be empty.

For making very infinite rose trees, consider

infTree labels = Rose labels (infForest labels)
infForest labels = [Rose labels' (infForest labels')
                      | labels' <- map (: labels) [0..]]

As chi points out, while this definition of paths is productive, it will in some cases repeat the leftmost path forever, and never reach any more. Oops! So some attempt at fairness or diagonal traversal is necessary to give interesting/useful results.

like image 34
dfeuer Avatar answered Oct 13 '22 12:10

dfeuer