Search for element in tree in Isabelle - binary-tree

I have this function testing wether an element is in a binary tree or not:
fun lookup::"int⇒bst⇒bool" where
"lookup x _ = false" |
"lookup x bst = ( if x = root(bst) then true else if x≤root(bst) then lookup x left(bst) else lookup x right(bst))"
I get the error message
Type unification failed:
Clash of types "bst" and "int"
The datatype bst is defined as
datatype bst = Leaf | Node int bst bst
What is wrong here?

It looks like your datatype declaration does not mention any associated value for a leaf. So, it might look like
datatype_new bst = Leaf int | Node int bst bst
Then the function just checks all the constructors of the current node:
fun lookup :: "int ⇒ bst ⇒ bool" where
"lookup x (Leaf y) = x = y" |
"lookup x (Node y leftbst rightbst) =
(if x = y then True
else (if x ≤ y then lookup x leftbst else lookup x rightbst))"

The short answer is that you're misusing root. It's NthRoot.root, from src/HOL/NthRoot.thy, and the type of root is shown in the error message:
root :: nat => real => real
I CNTL-clicked on root, and it took me to the function in NthRoot.thy.
From here, I basically complain that you made me work harder than I wanted, to answer your question, and even now, I'm assuming that I made me a THY which is duplicating what you're talking about.
You gave me this phrase in your comment: "imports Main Tree". However, after making a THY using only Main, the THY is obviously not what you're doing, because I don't get the error message Type unification failed: Clash of types "bst" and "nat".
theory Scratch
imports Main "~~/src/HOL/Library/Tree"
begin
datatype bst = Leaf | Node int bst bst
fun lookup :: "int ⇒ bst ⇒ bool" where
"lookup x _ = false" |
"lookup x bst = (if x = root(bst) then true else if x ≤ root(bst)
then lookup x left(bst) else lookup x right(bst))"
end
Also, in that THY, root is not defined, it's a variable. I saw that in two ways. I tried to CNTL-click on it, and nothing happened. I then noticed that it's blue, which means it's a local variable.
So, I imported Complex_Main like the following, and I got the error message that you're talking about. I know nothing much about binary trees, but the type of root shown in the error message can quickly tell you that root is most likely not what you want, since it's using real.
theory Scratch2
imports Complex_Main
begin
datatype bst = Leaf | Node int bst bst
fun lookup :: "int => bst => bool" where
"lookup x _ = false" |
"lookup x bst = (if x = root(bst) then true else if x ≤ root(bst)
then lookup x left(bst) else lookup x right(bst))"
(*Type unification failed: Clash of types "bst" and "nat"
Type error in application: incompatible operand type
Operator: root :: nat => real => real*)
end
Anyway, people don't want to see too much source in questions, and they don't want to see too little. If you provide the magic amount, and all they have to do is cut and paste, then they don't have to work as hard to answer your question.
From your last question, Predefined functions for Binary trees in Isabelle, I knew where to get Tree from what Andreas said.
Andreas is the answer man for people like me and you. If you want to increase the chances of someone like him answering questions, then you want him to have to work as little as possible to figure out your question.
A minimal working example can help make sure everyone is on the same page, and even catch some mistakes on your end before you ask a question.

Related

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
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.
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.
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.

Should I avoid constructing in Haskell?

While reading a snipped from Haskell for Great Good I found the following situation:
treeInsert :: (Ord a) => a -> Tree a -> Tree a
treeInsert x EmptyTree = singleton x
treeInsert x (Node a left right)
| x == a = Node x left right
| x < a = Node a (treeInsert x left) right
| x > a = Node a left (treeInsert x right)
Wouldn't it be better for performance if we just reused the given Tree when x == a?
treeInsert :: (Ord a) => a -> Tree a -> Tree a
treeInsert x EmptyTree = singleton x
treeInsert x all#(Node a left right)
| x == a = all
| x < a = Node a (treeInsert x left) right
| otherwise = Node a left (treeInsert x right)
In real life coding, what should I do? Are there any drawbacks when returning the same thing?
Let's look at the core! (Without optimisations here)
$ ghc-7.8.2 -ddump-simpl wtmpf-file13495.hs
The relevant difference is that the first version (without all#(...)) has
case GHC.Classes.> # a_aUH $dOrd_aUV eta_B2 a1_aBQ
of _ [Occ=Dead] {
GHC.Types.False ->
Control.Exception.Base.patError
# (TreeInsert.Tree a_aUH)
"wtmpf-file13495.hs:(9,1)-(13,45)|function treeInsert"#;
GHC.Types.True ->
TreeInsert.Node
# a_aUH
a1_aBQ
left_aBR
(TreeInsert.treeInsert # a_aUH $dOrd_aUV eta_B2 right_aBS)
where reusing the node with that as-pattern does just
TreeInsert.Node
# a_aUI
a1_aBR
left_aBS
(TreeInsert.treeInsert # a_aUI $dOrd_aUW eta_B2 right_aBT);
This is an avoided check that may well make a significant performance difference.
However, this difference has actually nothing to do with the as-pattern. It's just because your first snippet uses a x > a guard, which is not trivial. The second uses otherwise, which is optimised away.
If you change the first snippet to
treeInsert :: (Ord a) => a -> Tree a -> Tree a
treeInsert x EmptyTree = singleton x
treeInsert x (Node a left right)
| x == a = Node x left right
| x < a = Node a (treeInsert x left) right
| otherwise = Node a left (treeInsert x right)
then the difference boils down to
GHC.Types.True -> TreeInsert.Node # a_aUH a1_aBQ left_aBR right_aBS
vs
GHC.Types.True -> wild_Xa
Which is indeed just the difference of Node x left right vs all.
...without optimisations, that is. The versions diverge further when I turn on -O2. But I can't really make out how the performance would differ, there.
In real life coding, what should I do? Are there any drawbacks when returning the same thing?
a == b does not guarantee that f a == f b for all functions f. So, you may have to return new object even if they compare equal.
In other words, it may not be feasible to change Node x left right to Node a left right or all when a == x regardless of performance gains.
For example you may have types which carry meta data. When you compare them for equality, you may only care about the values and ignore the meta data. But if you replace them just because they compare equal then you will loose the meta data.
newtype ValMeta a b = ValMeta (a, b) -- value, along with meta data
deriving (Show)
instance Eq a => Eq (ValMeta a b) where
-- equality only compares values, ignores meta data
ValMeta (a, b) == ValMeta (a', b') = a == a'
The point is Eq type-class only says that you may compare values for equality. It does not guarantee anything beyond that.
A real-world example where a == b does not guarantee that f a == f b is when you maintain a Set of unique values within a self-balancing tree. A self-balancing tree (such as Red-Black tree) has some guarantees about the structure of tree but the actual depth and structure depends on the order that the data were added to or removed from the set.
Now when you compare 2 sets for equality, you want to compare that values within the set are equal, not that the underlying trees have the same exact structure. But if you have a function such as depth which exposes the depth of underlying tree maintaining the set then you cannot guarantee that the depths are equal even if the sets compare equal.
Here is a video of great Philip Wadler realizing live and on-stage that many useful relations do not preserve equality (starting at 42min).
Edit: Example from ghc where a == b does not imply f a == f b:
\> import Data.Set
\> let a = fromList [1, 2, 3, 4, 5, 10, 9, 8, 7, 6]
\> let b = fromList [1..10]
\> let f = showTree
\> a == b
True
\> f a == f b
False
Another real-world example is hash-table. Two hash-tables are equal if and only if their key-value pairs tie out. However, the capacity of a hash-table, i.e. the number of keys you may add before having to re-allocate and rehash, depends on the order of inserts/deletes.
So if you have a function which returns the capacity of hash table, it may return different values for hash-tables a and b even though a == b.
My two cents... perhaps not even about the original question:
Instead of writing guards with x < a and x == a, I would match compare a b against LT, EQ and GT, e.g.:
treeInsert x all#(Node a left right) =
case compare x a of
EQ -> ...
LT -> ...
GT -> ...
I would do this especially if x and a can be complex data structures, since a test like x < a could be expensive.
answer seems to be wrong. I just leave it here, for reference...
With your second function you avoid creating a new node, because the compiler cannot really understand equality (== is just some function.) If you change the first version to
-- version C
treeInsert :: (Ord a) => a -> Tree a -> Tree a
treeInsert x EmptyTree = singleton x
treeInsert x (Node a left right)
| x == a = Node a left right -- Difference here! Changed x to a.
| x < a = Node a (treeInsert x left) right
| x > a = Node a left (treeInsert x right)
the compiler will probably be able to do common subexpression elimination, because the optimizer will be able to see that Node a left right is the same as Node a left right.
On the other hand, I doubt that the compiler can deduce from a == x that Node a left right is the same as Node x left right.
So, I'm pretty sure that under -O2, version B and version C are the same, but version A is probably slower because it does an extra instantiation in the a == x case.
Well, if the first case had used a instead of x as follows, then there's at least the chance that GHC would eliminate the allocation of a new node through common subexpression elimination.
treeInsert x (Node a left right)
| x == a = Node a left right
However, this is all but irrelevant in any non-trivial use case, because the path down the tree to the node is going to be duplicated even when the element already exists. And this path is going to be significantly longer than a single node unless your use case is trivial.
In the world of ML, the fairly idiomatic way to avoid this is to throw a KeyAlreadyExists exception, and then catch that exception at the top-level insertion function and return the original tree. This would cause the stack to be unwound instead of allocating any of the Nodes on the heap.
A direct implementation of the ML idiom is basically a no-no in Haskell, for good reasons. If avoiding this duplication matters, the simplest and possibly best thing to do is to check if the tree contains the key before you insert it.
The downside of this approach, compared to a direct Haskell insert or the ML idiom, is that it involves two traversals of the path instead of one. Now, here is a non-duplicating, single-pass insert you can implement in Haskell:
treeInsert :: Ord a => a -> Tree a -> Tree a
treeInsert x original_tree = result_tree
where
(result_tree, new_tree) = loop x original_tree
loop x EmptyTree = (new_tree, singleton x)
loop x (Node a left right) =
case compare x a of
LT -> let (res, new_left) = loop x left
in (res, Node a new_left right)
EQ -> (original_tree, error "unreachable")
GT -> let (res, new_right) = loop x right
in (res, Node a left new_right)
However, older versions of GHC (roughly 7-10 years ago) don't handle this sort of recursion through lazy pairs of results very efficiently, and in my experience check-before-insert is likely to perform better. I'd be slightly surprised if this observation has really changed in the context of more recent GHC versions.
One can certainly imagine a function that directly constructs (but does not return) a new path for the tree, and decides to return the new path or the original path once it's known whether the element exists already. (The new path would immediately become garbage if it is not returned.) This conforms to the basic principles of the GHC runtime, but isn't really expressible in the source language.
Of course, any completely non-duplicating insertion function on a lazy data structure is going to have different strictness properties than a simple, duplicating insert. So no matter the implementation technique, they are different functions if laziness matters.
But of course, whether or not the path is duplicated may not matter that much. The cases where it would matter the most would be when you are using the tree persistently, because in linear use cases the old path would become garbage immediately after each insertion. And of course, this only matters when you are inserting a significant number of duplicates.

Isabelle: Issue with leibniz formula

As I understand it, matrices in Isabelle are essentially functions and of abitrary dimension. In this setting, it is not easy to define a squared matrix (n x n matrix). Also, in a proof on paper the dimension "n" of a squared can be used in the proof. But how do I do that in Isabelle?
Leibniz Formula:
My proof on paper:
Here is a relevant excerpt of my Isabelle proof:
(* tested with Isabelle2013-2 (and also Isabelle2013-1) *)
theory Notepad
imports
Main
"~~/src/HOL/Library/Polynomial"
"~~/src/HOL/Multivariate_Analysis/Determinants"
begin
notepad
begin
fix C :: "('a::comm_ring_1 poly)^'n∷finite^'n∷finite"
(* Definition Determinant (from the HOL Library, shown for reference
see: "~~/src/HOL/Multivariate_Analysis/Determinants") *)
have "det C =
setsum (λp. of_int (sign p) *
setprod (λi. C$i$p i) (UNIV :: 'n set))
{p. p permutes (UNIV :: 'n set)}" unfolding det_def by simp
(* assumtions *)
have 1: "∀ i j. degree (C $ i $ j) ≤ 1" sorry (* from assumtions, not shown *)
have 2: "∀ i. degree (C $ i $ i) = 1" sorry (* from assumtions, not shown *)
(* don't have "n", that is the dimension of the squared matrix *)
have "∀p∈{p. p permutes (UNIV :: 'n set)}. degree (setprod (λi. C$i$p i) (UNIV :: 'n set)) ≤ n" sorry (* no n! *)
end
What can I do in this situation?
UPDATE:
Your type for C, a restricted version of ('a ^ 'n ^ 'n), appears to be a custom type of > yours, because I get an error when trying to use it, even after importing > Polynomial.thy. But maybe it's defined in some other HOL theory.
Unfortunately I did not write the includes in my code example, please see the updated example. But it is not a custom type, importing "Polynomial.thy" and "Determinants" should be sufficient. (I tested Isabelle version 2013-1 and 2013-2.)
If you're using a custom definition of a matrix, there's a good chance
you're on your own, for the most part.
I don't belive I am using a custom definition of a matrix.
The library Determinants (~~/src/HOL/Multivariate_Analysis/Determinants) has the following definition of a determinant:
definition det:: "'a::comm_ring_1^'n^'n ⇒ 'a" where .... So the library uses the notion of a matrix as a vector of vectors. If my ring is over polynomials it should not make a difference in my eyes.
Regardless, for a type such as ('a ^ 'n ^ 'n), it seems to me, you
should be able to write a function to return a value for the size of
the matrix. So if (p ^ n ^ n) is a matrix, where n is a set, then
maybe the cardinality of n is the n you want in your question.
This brought me on the right way. My current guess is that the following definition is helpful:
definition card_diagonal :: "('a::zero poly)^'n^'n ⇒ nat" where "card_diagonal A = card { (A $ i $ i) | i . True }"
card is definied in Finite_Set.
It seems to me that the essence of this question is how to obtain the integer n from a given n x n matrix, A. The difficulty here is that this integer is encoded in A's type. Nevertheless, it seems clear to me that n is actually a parameter of the problem. Although we can imagine representations of matrices that somehow store the dimension internally, from a mathematical point of view, it is natural to begin the entire development by stating "let n be a positive integer".
Update 140107_2040
It's hard to make a short answer here. I only work everything for vectors, since it all gets very involved. I try to give you the function for the length of a vector as fast as possible. I then go into a big explanation on what I did to get a decent understanding of the vector type, but not necessarily for you, if you don't need it.
Reflected by the name Finite_Cartesian_Product.thy, Amine Chaieb defines a generalized finite Cartesian product. So, of course, we also get a definition for vectors and n-tuples. That it's a generalized Cartesian product is what requires the huge explanation, and what took me a long time to recognize and work through. Having said that, I'll call it a vector, since he named the type vec.
Everything needs to be understood in reference to what a vector is, which is defined by this definition:
typedef ('a, 'b) vec = "UNIV :: (('b::finite) => 'a) set"
This tells us that a vector is a function f::('b::finite) => 'a. The domain of the function is UNIV::'b set, which is finite and is called the index set. For example, let the index set be defined with typedef as {1,2,3}.
The codomain of the function can be any type, but let it be a set of constants {a,b}, defined with typedef. Because HOL functions are total, each element of {1,2,3} must get mapped to an element of {a,b}.
Now, consider the set of all such functions that map elements from {1,2,3} to {a,b}. There will be 2^3 = 8 such functions. I now resort to ZFC function notation, along with n-tuple notation:
f_1: {1,2,3} --> {a,b} == {(1,a),(2,a),(3,a)} == (a,a,a)
f_2 == {(1,a),(2,a),(3,b)} == (a,a,b)
f_3 == {(1,a),(2,b),(3,a)} == (a,b,a)
f_4 == {(1,a),(2,b),(3,b)} == (a,b,b)
f_5 to f_8 == (b,a,a), (b,a,b), (b,b,a), (b,b,b)
Then for any vector f_i, which, again, is a function, the length of the vector will be the cardinality of the domain of f_i, which will be 3.
I'm pretty sure your function card_diagonal is the cardinality of the range of the function, and I tested out a vector version of it much further down, but it basically showed me how to get the cardinality of the domain.
Here is the function for the length of a vector:
definition vec_length :: "('a, 'b::finite) vec => nat" where
"vec_length v = card {i. ? c. c = (vec_nth v) i}"
declare
vec_length_def [simp add]
You might want to substitute v $ i for (vec_nth v) i. The ? is \<exists>.
In my example below, the simp method easily produced a goal CARD(t123) = (3::nat), where t123 is a type I defined with 3 elements in it. I couldn't get past that.
Anyone who wants to understand the details needs to understand the use of the Rep_t and Abs_t functions that are created when typedef is used to create a type t. In the case of vec, the functions would have been Rep_vec and Abs_vec, but they are renamed with morphisms to vec_nth and vec_lambda.
Will the Non-vector-specific Vector Length Please Step Forward
Update 140111
This should be my final update, because to completely work it out to my satisfaction, I need to know much more about instantiating type classes in general, and how to specifically instantiate type classes so that my concrete example, UNIV::t123 set, is finite.
I more than welcome being corrected where I may be wrong. I would much rather be reading about Multivariate_Analysis in a textbook than be learning how to use Isar and Isabelle/HOL like this.
By all appearances, the concept of the length of a vector of type ('a, 'b) vec is extraordinarily simple. It is the cardinality of the universal set of the type 'b::finite.
Intuitively, it makes sense, so I commit to the idea prematurely, but I don't permanently commit because I can't finish my example.
I added an update to the end of my "investigative" theory below.
What I hadn't done before is instantiate my example type, t123, a type defined with the set {c1,c2,c3}, as type class top.
The shorter story is that in pursuing top, value tipped me off that type class card_UNIV is involved, where card_UNIV is based on finite_UNIV. Again, the descriptive identifiers make it seem that if my type t123 is of type class finite_UNIV, then I can calculate the cardinality of it with card, which will be the length of any vector using type t123 as the index set.
I show some terms here which indicate what's involved, which, as usual, can be investigated by cntl-clicking on various identifiers, if you have my example theory loaded. A little more detail is in my investigative source below.
term "UNIV::t123 set"
term "top::t123 set"
term "card (UNIV::t123 set)" (*OUTPUT PANEL: CARD(t123)::nat.*)
term "card (top::t123 set)" (*OUTPUT PANEL: CARD(t123)::nat.*)
value "card (top::t123 set)" (*ERROR: Type t123 not of sort card_UNIV.*)
term "card_UNIV"
term "finite_UNIV"
(End of update.)
140112 Final update to the final update
It paid to not permanently commit, and though answering questions is a good way to learn, there is also downside under these circumstances.
For the vector type, the only type class that's part of the definition is finite, but then, above, what I'm doing involves type class finite_UNIV, which is in src/HOL/Library/Cardinality.thy.
Trying to use card, like with card (UNIV::t123 set), won't work for type vec because you can't assume that type class finite_UNIV has been instantiated for the index set type. If I'm wrong here with what seems to be obvious now, I'd like to know.
Well, even though the function I defined, vector_length, doesn't try to take the cardinality of UNIV::'b set directly, with my example, the simplifier produces the goal CARD(t123) = (3::nat).
I speculate on what that means for myself, but I haven't tracked down CARD, so I keep my speculations to myself.
(End of update.)
140117 Final final final
Trying to use value to learn about the use of card led me astray. The value command is based on the code generator, and value will have type class requirements that aren't needed in general.
There's no requirement that the index set be instantiated for type class finite_UNIV. It's just that the logic needed to be able to use card (UNIV::('b::finite set)) has to be in place.
It seems like the logic should already be there in Multivariate_Analysis for anything I've done. Anything I've said is subject to error.
(End of update.)
Conclusion About My Experience Here with vec in Multivariate_Analysis
Using generalized index sets seems overly complex, at least for me. Vectors as lists seems like what I would want, like with Matrix.thy, but maybe things need to be complex at times.
The biggest pain is using typedef to create a type which has a finite universal set. I don't know how to easily create finite sets. I saw a comment in the past that it's best to stay away from typedef. It sounds good at first, that it creates a type based on a set, but it ends up being a hassle to deal with.
[I comment further here about finite, generalized index sets being used in vec. I have to resort to a ZFC definition, because I have no idea where textbooks are that formalize general mathematics with type theory. This wiki article shows a generalized Cartesian product:
Wiki: Infinite product definition using a finite or infinite index set
Key to the definition is that an infinite set can be used as the index set, such as the real numbers.
As far as using a finite set as an index set, any finite set of cardinality n can be put one-to-one with the natural numbers 1...n, and a finite, natural number ordering is normally how we would use a vector.
It's not that I don't believe that someone, somewhere needs vectors with a finite index set that's not the natural numbers, but all the math I've seen for vectors and matrices is vectors of length n::nat, or n::nat x m::nat matrices.
For myself, I would think that the best vector and matrix would be based on list, since the component location of a list is based on natural numbers. There's a lot of computational magic that comes from using an Isabelle/HOL list.]
What I worked Through to Get the Above
It took me a lot of work to work through this. I know much less of how to use Isabelle than much more.
(*It's much faster to start jEdit with Multivariate_Analysis as the logic.*)
theory i140107a__Multvariate_Ana_vec_length
imports Complex_Main Multivariate_Analysis (*"../../../iHelp/i"*)
begin
declare[[show_sorts=true]] (*Set false if you don't want typing shown.*)
declare[[show_brackets=true]]
(*---FINITE UNIVERSAL SET, NOT FINITE SET
*)
(*
First, we need to understand what `x::('a::finite)` means. It means that
`x` is a type for which the universal set of it's type is finite, where
the universal set is `UNIV::('a set)`. It does not mean that terms of type
`'a::finite` are finite sets.
The use of `typedef` below will hopefully make this clear. The following are
related to all of this, cntl-click on them to investigate them.
*)
term "x::('a::finite)"
term "finite::('a set => bool)" (*the finite predicate*)
term "UNIV::('a set) == top" (*UNIV is designated universal set in Set.thy.*)
term "finite (UNIV :: 'a set)"
term "finite (top :: 'a set)"
(*
It happens to be that the `finite` predicate is used in the definition of
type class `finite`. Here are some pertinent snippets, after which I comment
on them:
class top =
fixes top :: 'a ("⊤")
abbreviation UNIV :: "'a set" where
"UNIV == top"
class finite =
assumes finite_UNIV: "finite (UNIV :: 'a set)"
The `assumes` in the `finite` type-class specifies that constant `top::'a set`
is finite, where `top` can be seen as defined in type-class `top`. Thus, any
type of type-class `top` must have a `top` constant.
The constant `top` is in Orderings.thy, and the Orderings theory comes next
after HOL.thy, which is fundamental. As to why this use of the constant `top`
by type-class `finite` can make the universe of a type finite, I don't know.
*)
(*---DISCOVERING LOWER LEVEL SYNTAX TO WORK WITH
*)
(*
From the output panel, I copied the type shown for `term "v::('a ^ 'b)"`. I
then cntl-clicked on `vec` to take me to the `vec` definition.
*)
term "v::('a ^ 'b)"
term "v::('a,'b::finite) vec"
(*
The `typedef` command defines the `('a, 'b) vec` type as an element of a
particular set, in particular, as an element in the set of all functions of
type `('b::finite) => 'a`. I rename `vec` to `vec2` so I can experiment with
`vec2`.
*)
typedef ('a, 'b) vec2 = "UNIV :: (('b::finite) => 'a) set"
by(auto)
notation
Rep_vec2 (infixl "$$" 90)
(*
The `morphisms` command renamed `Rep_vec` and `Abs_vec` to `vec_nth` and
`vec_lambda`, but I don't rename them for `vec2`. To create the `vec_length`
function, I'll be using the `Rep` function, which is `vec_nth` for `vec`.
However, the `Abs` function comes into play further down with the concrete
examples. It's used to coerce a function into a type that uses the type
construcor `vec`.
*)
term "Rep_vec2::(('a, 'b::finite) vec2 => ('b::finite => 'a))"
term "Abs_vec2::(('a::finite => 'b) => ('b, 'a::finite) vec2)"
(*---FIGURING OUT HOW THE REP FUNCTION WORKS WITH 0, 1, OR 2 ARGS
*)
(*
To figure it all out, I need to study these Rep_t function types. The type
of terms without explicit typing have the type shown below them, with the
appropriate `vec` or `vec2`.
*)
term "op $"
term "vec_nth"
term "op $$"
term "Rep_vec2::(('a, 'b::finite) vec2 => ('b::finite => 'a))"
term "op $ x"
term "vec_nth x"
term "op $$ x"
term "(Rep_vec2 x)::('b::finite => 'a)"
term "x $ i"
term "op $ x i"
term "vec_nth x i"
term "x $$ i"
term "op $$ x i"
term "(Rep_vec2 (x::('a, 'b::finite) vec2) (i::('b::finite))) :: 'a"
(*
No brackets shows more clearly that `x $$ i` is the curried function
`Rep_vec2` taking the arguments `x::(('a, 'b::finite) vec2)` and
`i::('b::finite)`.
*)
term "Rep_vec2::('a, 'b::finite) vec2 => 'b::finite => 'a"
(*---THE FUNCTION FOR THE LENGTH OF A VECTOR*)
(*
This is based on your `card_diagonal`, but it's `card` of the range of
`vec_nth v`. You want `card` of the domain.
*)
theorem "{ (v $ i) | i. True } = {c. ? i. c = (v $ i)}"
by(simp)
definition range_size :: "('a, 'b::finite) vec => nat" where
"range_size v = card {c. ? i. c = (v $ i)}"
declare
range_size_def [simp add]
(*
This is the card of the domain of `(vec_nth v)::('b::finite => 'a)`. I use
`vec_nth v` just to emphasize that what we want is `card` of the domain.
*)
theorem "(vec_nth v) i = (v $ i)"
by(simp)
definition vec_length :: "('a, 'b::finite) vec => nat" where
"vec_length v = card {i. ? c. c = (vec_nth v) i}"
declare
vec_length_def [simp add]
theorem
"∀x y. vec_length (x::('a, 'b) vec) = vec_length (y::('a, 'b::finite) vec)"
by(simp)
(*---EXAMPLES TO TEST THINGS OUT
*)
(*
Creating some constants.
*)
typedecl cT
consts
c1::cT
c2::cT
c3::cT
(*
Creating a type using the set {c1,c2,c3}.
*)
typedef t123 = "{c1,c2,c3}"
by(auto)
(*
The functions Abs_t123 and Rep_t123 are created. I have to use Abs_t123 below
to coerce the type of `cT` to `t123`. Here, I show the type of `Abs_t123`.
*)
term "Abs_t123 :: (cT => t123)"
term "Abs_t123 c1 :: t123"
(*
Use these `declare` commands to do automatic `Abs` coercion. I comment
them out to show how I do coercions explicitly.
*)
(*declare [[coercion_enabled]]*)
(*declare [[coercion Abs_t123]]*)
(*
I have to instantiate type `t123` as type-class `finite`. It seems it should
be simple to prove, but I can't prove it, so I use `sorry`.
*)
instantiation t123 :: finite
begin
instance sorry
end
term "UNIV::t123 set"
term "card (UNIV::t123 set)"
theorem "card (UNIV::t123 set) = 3"
try0
oops
(*
Generalized vectors use an index set, in this case `{c1,c2,c3}`. A vector is
an element from the set `(('b::finite) => 'a) set`. Concretely, my vectors are
going to be from the set `(t123 => nat) set`. I define a vector by defining a
function `t123_to_0`. Using normal vector notation, it is the vector
`<0,0,0>`. Using ZFC ordered pair function notation, it is the set
{(c1,0),(c2,0),(c3,0)}.
*)
definition t123_to_0 :: "t123 => nat" where
"t123_to_0 x = 0"
declare
t123_to_0_def [simp add]
(*
I'm going to have to use `vec_lambda`, `vec_nth`, and `Abs_t123`, so I create
some `term` variations to look at types in the output panel, to try to figure
out how to mix and match functions and arguments.
*)
term "vec_lambda (f::('a::finite => 'b)) :: ('b, 'a::finite) vec"
term "vec_lambda t123_to_0 :: (nat, t123) vec"
term "vec_nth (vec_lambda t123_to_0)"
term "vec_nth (vec_lambda t123_to_0) (Abs_t123 c1)"
(*
The function `vec_length` seems to work. You'd think that `CARD(t123) = 3`
would be true. I try to cntl-click on `CARD`, but it doesn't work.
*)
theorem "vec_length (vec_lambda t123_to_0) = (3::nat)"
apply(simp)
(*GOAL: (CARD(t123) = (3::nat))*)
oops
theorem "(vec_nth (vec_lambda t123_to_0) (Abs_t123 c1)) = (0::nat)"
by(auto)
theorem "range_size (vec_lambda t123_to_0) = (1::nat)"
by(auto)
definition t123_to_x :: "t123 => t123" where
"t123_to_x x = x"
declare
t123_to_x_def [simp add]
theorem "(vec_nth (vec_lambda t123_to_x) (Abs_t123 c1)) = (Abs_t123 c1)"
by(auto)
theorem "(vec_nth (vec_lambda t123_to_x) (Abs_t123 c2)) = (Abs_t123 c2)"
by(auto)
(*THE LENGTH BASED SOLELY ON THE TYPE, NOT ON A PARTICULAR VECTOR
*)
(*Update 140111: The length of a vector is going to be the cardinality of the
universal set of the type, `UNIV::('a::finite set)`. For `t123`, the following
terms are involved.
*)
term "UNIV::t123 set"
term "top::t123 set"
term "card (UNIV::t123 set)" (*OUTPUT PANEL: CARD(t123)::nat.*)
term "card (top::t123 set)" (*OUTPUT PANEL: CARD(t123)::nat.*)
(*
It can be seen that `card (top::t123 set)` is the same as the theorem above
with the goal `CARD(t123) = (3::nat)`. What I didn't do above is instantiate
type `t123` for type-class `top`. I try to define `top_t123`, but it gives me
an error.
*)
instantiation t123 :: top
begin
definition top_t123 :: "t123 set" where
"top_t123 = {Abs_t123 c1, Abs_t123 c2, Abs_t123 c3}"
(*ERROR
Clash of specifications
"i140107a__Multvariate_Ana_vec_length.top_set_inst.top_set_def" and
"Set.top_set_inst.top_set_def" for constant "Orderings.top_class.top"
*)
instance sorry
end
(*To define the cardinality of type `t123` appears to be an involved process,
but maybe there's one easy type-class that can be instantiated that gives me
everything I need. The use of `value` shows that type `t123` needs to be
type-class `card_UNIV`, but `card_UNIV` is based on class `finite_UNIV`.
Understanding it all is involved enough to give job security to a person who
does understand it.
*)
value "card (top::t123 set)" (*ERROR: Type t123 not of sort card_UNIV.*)
term "card_UNIV"
term "finite_UNIV"
(******************************************************************************)
end
The First Parts of My Answer
(Because the imports weren't shown for the source, it wasn't obvious where any of the operators were coming from. There's also the Matrix AFP entry to confuse things. Additionally, other than atomic constants and variables in HOL, most everything is a function, so classifying something as a function doesn't clarify anything without some context. Providing source that won't produce errors helps. The normal entry point is Complex_Main. That sums up most of what I had said here. )
Links to Related Questions
[13-05-27] Isabelle: how to work with matrices
[13-05-30] Isabelle: transpose a matrix that includes a constant factor
[13-06-25] Isabelle matrix arithmetic: det_linear_row_setsum in library with different notation
[13-08-12] Isabelle: maximum value in a vector
[13-09-12] Degree of polynomial smaller than a number
[13-11-21] Isabelle: degree of polynomial multiplied with constant
[13-11-26] Isabelle: Power of a matrix (A^n)?
[13-12-01] Isabelle: difference between A * 1 and A ** mat 1
[14-01-17] Isabelle: Issue with setprod

Why are (constant) expressions not evaluated at compile time in Haskell?

I am currently learning Haskell, and there is one thing that baffles me:
When I build a complex expression (whose computation will take some time) and this expression is constant (meaning it is build only of known, hard coded values), the expression is not evaluated at compile time.
Comming from a C/C++ background I am used to such kind of optimization.
What is the reason to NOT perform such optimization (by default) in Haskell / GHC ? What are the advantages, if any?
data Tree a =
EmptyTree
| Node a (Tree a) (Tree a)
deriving (Show, Read, Eq)
elementToTree :: a -> Tree a
elementToTree x = Node x EmptyTree EmptyTree
treeInsert :: (Ord a) => a -> Tree a -> Tree a
treeInsert x EmptyTree = elementToTree x
treeInsert x (Node a left right)
| x == a = Node x left right
| x < a = Node a (treeInsert x left) right
| x > a = Node a left (treeInsert x right)
treeFromList :: (Ord a) => [a] -> Tree a
treeFromList [] = EmptyTree
treeFromList (x:xs) = treeInsert x (treeFromList xs)
treeElem :: (Ord a) => a -> Tree a -> Bool
treeElem x EmptyTree = False
treeElem x (Node a left right)
| x == a = True
| x < a = treeElem x left
| x > a = treeElem x right
main = do
let tree = treeFromList [0..90000]
putStrLn $ show (treeElem 3 tree)
As this will always print True I would expect the compiled programm to print and exit
almost immediately.
You may like this reddit thread. The compiler could try to do this, but it could be dangerous, as constants of any type can do funny things like loop. There are at least two solutions: one is supercompilation, not available as part of any compiler yet but you can try prototypes from various researchers; the more practical one is to use Template Haskell, which is GHC's mechanism for letting the programmer ask for some code to be run at compile time.
The process you are talking about is called supercompilation and it's more difficult than you make it out to be. It is actually one of the active research topics in computing science! There are some people that are trying to create such a supercompiler for Haskell (probably based on GHC, my memory is vague) but the feature is not included in GHC (yet) because the maintainers want to keep compilation times down. You mention C++ as a language that does this – C++ also happens to have notoriously bad compilation times!
Your alternative for Haskell is to do this optimisation manually with Template Haskell, which is Haskells compile-time evaluated macro system.
In this case, GHC can not be sure that the computation would finish. It's not a question of lazy versus strict, but rather the halting problem. To you, it looks quite simple to say that treeFromlist [0..90000] is a constant that can be evaluated at compile time, but how does the compiler know this? The compiler can easily optimize [0..90000] to a constant, but you wouldn't even notice this change.

Can I always convert mutable-only algorithms to single-assignment and still be efficient?

The Context
The context of this question is that I want to play around with Gene Expression Programming (GEP), a form of evolutionary algorithm, using Erlang. GEP makes use of a string based DSL called 'Karva notation'. Karva notation is easily translated into expression parse trees, but the translation algorithm assumes an implementation having mutable objects: incomplete sub-expressions are created early-on the translation process and their own sub-expressions are filled-in later-on with values that were not known at the time they were created.
The purpose of Karva notation is that it guarantees syntactically correct expressions are created without any expensive encoding techniques or corrections of genetic code. The problem is that with a single-assignment programming language like Erlang, I have to recreate the expression tree continually as each sub expression gets filled in. This takes an inexpensive - O(n)? - update operation and converts it into one that would complete in exponential time (unless I'm mistaken). If I can't find an efficient functional algorithm to convert K-expressions into expression trees, then one of the compelling features of GEP is lost.
The Question
I appreciate that the K-expression translation problem is pretty obscure, so what I want is advice on how to convert an inherently-non-functional algorithm (alg that exploits mutable data structures) into one that does not. How do pure functional programming languages adapt many of the algorithms and data structures that were produced in the early days of computer science that depend on mutability to get the performance characteristics they need?
Carefully designed immutability avoids unecessary updating
Immutable data structures are only an efficiency problem if they're constantly changing, or you build them up the wrong way. For example, continually appending more to the end of a growing list is quadratic, whereas concatenating a list of lists is linear. If you think carefully, you can usually build up your structure in a sensible way, and lazy evaluation is your friend - hand out a promise to work it out and stop worrying.
Blindly trying to replicate an imperative algorithm can be ineffecient, but you're mistaken in your assertion that functional programming has to be asymptotically bad here.
Case study: pure functional GEP: Karva notation in linear time
I'll stick with your case study of parsing Karva notation for GEP. (
I've played with this solution more fully in this answer.)
Here's a fairly clean pure functional solution to the problem. I'll take the opportunity to name drop some good general recursion schemes along the way.
Code
(Importing Data.Tree supplies data Tree a = Node {rootLabel :: a, subForest :: Forest a} where type Forest a = [Tree a].)
import Data.Tree
import Data.Tree.Pretty -- from the pretty-tree package for visualising trees
arity :: Char -> Int
arity c
| c `elem` "+*-/" = 2
| c `elem` "Q" = 1
| otherwise = 0
A hylomorphism is the composition of an anamorphism (build up, unfoldr) and a catamorphism (combine, foldr).
These terms are introduced to the FP community in the seminal paper Functional Programming with Bananas, Lenses and Barbed wire.
We're going to pull the levels out (ana/unfold) and combine them back together (cata/fold).
hylomorphism :: b -> (a -> b -> b) -> (c -> (a, c)) -> (c -> Bool) -> c -> b
hylomorphism base combine pullout stop seed = hylo seed where
hylo s | stop s = base
| otherwise = combine new (hylo s')
where (new,s') = pullout s
To pull out a level, we use the total arity from the previous level to find where to split off this new level, and pass on the total arity for this one ready for next time:
pullLevel :: (Int,String) -> (String,(Int,String))
pullLevel (n,cs) = (level,(total, cs')) where
(level, cs') = splitAt n cs
total = sum $ map arity level
To combine a level (as a String) with the level below (that's already a Forest), we just pull off the number of trees that each character needs.
combineLevel :: String -> Forest Char -> Forest Char
combineLevel "" [] = []
combineLevel (c:cs) levelBelow = Node c subforest : combineLevel cs theRest
where (subforest,theRest) = splitAt (arity c) levelBelow
Now we can parse the Karva using a hylomorphism. Note that we seed it with a total arity from outside the string of 1, since there's only one node at the root level. Correspondingly we apply head to the result to get this singleton back out after the hylomorphism.
karvaToTree :: String -> Tree Char
karvaToTree cs = let
zero (n,_) = n == 0
in head $ hylomorphism [] combineLevel pullLevel zero (1,cs)
Linear Time
There's no exponential blowup, nor repeated O(log(n)) lookups or expensive modifications, so we shouldn't be in too much trouble.
arity is O(1)
splitAt part is O(part)
pullLevel (part,cs) is O(part) for grab using splitAt to get level, plus O(part) for the map arity level, so O(part)
combineLevel (c:cs) is O(arity c) for the splitAt, and O(sum $ map arity cs) for the recursive call
hylomorphism [] combineLevel pullLevel zero (1,cs)
makes a pullLevel call for each level, so the total pullLevel cost is O(sum parts) = O(n)
makes a combineLevel call for each level, so the total combineLevel cost is O(sum $ map arity levels) = O(n), since the total arity of the entire input is bound by n for valid strings.
makes O(#levels) calls to zero (which is O(1)), and #levels is bound by n, so that's below O(n) too
Hence karvaToTree is linear in the length of the input.
I think that puts to rest the assertion that you needed to use mutability to get a linear algorithm here.
Demo
Let's have a draw of the results (because Tree is so full of syntax it's hard to read the output!). You have to cabal install pretty-tree to get Data.Tree.Pretty.
see :: Tree Char -> IO ()
see = putStrLn.drawVerticalTree.fmap (:"")
ghci> karvaToTree "Q/a*+b-cbabaccbac"
Node {rootLabel = 'Q', subForest = [Node {rootLabel = '/', subForest = [Node {rootLabel = 'a', subForest = []},Node {rootLabel = '*', subForest = [Node {rootLabel = '+', subForest = [Node {rootLabel = '-', subForest = [Node {rootLabel = 'b', subForest = []},Node {rootLabel = 'a', subForest = []}]},Node {rootLabel = 'c', subForest = []}]},Node {rootLabel = 'b', subForest = []}]}]}]}
ghci> see $ karvaToTree "Q/a*+b-cbabaccbac"
Q
|
/
|
------
/ \
a *
|
-----
/ \
+ b
|
----
/ \
- c
|
--
/ \
b a
which matches the output expected from this tutorial where I found the example:
There isn't a single way to do this, it really has to be attempted case-by-case. I typically try to break them down into simpler operations using fold and unfold and then optimize from there. Karva decoding case is a breadth-first tree unfold as others have noted, so I started with treeUnfoldM_BF. Perhaps there are similar functions in Erlang.
If the decoding operation is unreasonably expensive, you could memoize the decoding and share/reuse subtrees... though it probably wouldn't fit into a generic tree unfolder and you'd need to write specialized function to do so. If the fitness function is slow enough, it may be fine to use a naive decoder like the one I have listed below. It will fully rebuild the tree each invocation.
import Control.Monad.State.Lazy
import Data.Tree
type MaxArity = Int
type NodeType = Char
treeify :: MaxArity -> [Char] -> Tree NodeType
treeify maxArity (x:xs) = evalState (unfoldTreeM_BF (step maxArity) x) xs
treeify _ [] = fail "empty list"
step :: MaxArity -> NodeType -> State [Char] (NodeType, [NodeType])
step maxArity node = do
xs <- get
-- figure out the actual child node count and use it instead of maxArity
let (children, ys) = splitAt maxArity xs
put ys
return (node, children)
main :: IO ()
main = do
let x = treeify 3 "0138513580135135135"
putStr $ drawTree . fmap (:[]) $ x
return ()
There are a couple of solutions when mutable state in functional programming is required.
Use a different algorithm that solves the same problem. E.g. quicksort is generally regarded as mutable and may therefore be less useful in a functional setting, but mergesort is generally better suited for a functional setting. I can't tell if this option is possible or makes sense in your case.
Even functional programming languages usually provide some way to mutate state. (This blog post seems to show how to do it in Erlang.) For some algorithms and data structures this is indeed the only available option (there's active research on the topic, I think); for example hash tables in functional programming languages are generally implemented with mutable state.
In your case, I'm not so sure immutability really leads to a performance bottleneck. You are right, the (sub)tree will be recreated on update, but the Erlang implementation will probably reuse all the subtrees that haven't changed, leading to O(log n) complexity per update instead of O(1) with mutable state. Also, the nodes of the trees won't be copied but instead the references to the nodes, which should be relatively efficient. You can read about tree updates in a functional setting in e.g. the thesis from Okasaki or in his book "Purely Functional Data Structures" based on the thesis. I'd try implementing the algorithm with an immutable data structure and switch to a mutable one if you have a performance problem.
Also see some relevant SO questions here and here.
I think I figured out how to solve your particular problem with the K trees, (the general problem is too hard :P). My solution is presented in some horrible sort of hybrid Python-like psudocode (I am very slow on my FP today) but it doesn't change a node after you create one (the trick is building the tree bottom-up)
First, we need to find which nodes belong to which level:
levels currsize nodes =
this_level , rest = take currsize from nodes, whats left
next_size = sum of the arities of the nodes
return [this_level | levels next_size rest]
(initial currsize is 1)
So in the +/*abcd, example, this should give you [+, /*, abcd]. Now you can convert this into a tree bottom up:
curr_trees = last level
for level in reverse(levels except the last)
next_trees = []
for root in level:
n = arity of root
trees, curr_trees = take n from curr_trees, whats left
next_trees.append( Node(root, trees) )
curr_trees = next_trees
curr_trees should be a list with the single root node now.
I am pretty sure we can convert this into single assignment Erlang/Haskell very easily now.

Resources