Wiebe-Marten Wijnja
Theory
alphabet: A set of symbols \(V\)
axiom: starting string \(\omega \in V^+\)
production rules: \(P \subset V \times V^+\)
Each rule is usually written as \( a \rightarrow \chi \) where:
Context-free L-system \(G = \langle V, \omega, P \rangle\)
Deterministic Context-Free ('D0')
more powerful classes of L-systems expand on this
Theory
alphabet: A set of symbols \(V\)
axiom: starting string \(\omega \in V^+\)
production rules: \(P \subset V \times V^+\)
Each rule is usually written as \( a \rightarrow \chi \) where:
Context-free L-system \(G = \langle V, \omega, P \rangle\)
Generation: transforming $$ \mu = a_1 a_2\ldots a_n $$ into
$$ \nu = \chi_1 \chi_2 \ldots \chi_n $$
... and repeat as desired
to arbitrary depth \(d\)
Deterministic Context-Free ('D0')
more powerful classes of L-systems expand on this
0
1
2
3
Example: Algae
Example: Plant
Algorithm
rewriteString(rules: LSystemRules, string: String) {
string
|> map(|symbol| lookupSymbol(rules, symbol))
|> flatten()
}
sequentialRewrite(rules: LSystemRules, string: String, depth: ℤ) {
if depth == 0 {
string
} else {
let result <- rewriteString(string, rules);
sequentialRewrite(rules, result, depth - 1)
}
}
Algorithm
rewriteString(rules: LSystemRules, string: String) {
string
|> map(|symbol| lookupSymbol(rules, symbol))
|> flatten()
}
sequentialRewrite(rules: LSystemRules, string: String, depth: ℤ) {
if depth == 0 {
string
} else {
let result <- rewriteString(string, rules);
sequentialRewrite(rules, result, depth - 1)
}
}
Oof \(\longrightarrow\)
Problems
Problems
Lipp-Wonka-Wimmer
lippWonkaWimmerRewrite(rules: LSystemRules, string: String, depth: ℤ)
if depth == 0 {
string
} else {
let rewrite_lengths <- scatterJoin(string, |string_part|
rewriteLength(string_part, rules)
);
let mut result <- allocateString(∑(rewrite_lengths));
let indexes <- prefixSum(rewrite_lengths);
scatterJoin(⧼string, indexes, rewrite_lengths⧽, |⧼string_part, start_index, length⧽|
let end_index <- start_index + length;
result[start_index..end_index] <- rewriteString(string_part, rules);
);
lippWonkaWimmerRewrite(rules, result, depth - 1)
}
\(\uparrow\) Always the same!
Still holds true for more powerful classes of L-systems,
with some exceptions
treeConquerRewrite(rules: LSystemRules, string: String, depth: ℤ){
if(depth == 0) {
string
} else if(*small amount of work*) {
let result <- rewriteString(string, rules);
treeConquerRewrite(rules, result, depth - 1);
} else { /* string is long, large amount of work */
let (left_half, right_half) <- splitInHalf(string);
let (left_res, right_res) <- forkJoin(
|| treeConquerRewrite(rules, left_half, depth),
|| treeConquerRewrite(rules, right_half, depth)
);
concatenate(left_res, right_res)
}
}
penultimateRewrite(rules: LSystemRules, string: String, depth: ℤ) {
if depth == 0 {
string
} else {
let mut flat_rules <- rules;
for depth > 1; depth <- depth - 1 {
flat_rules <- flattenRules(rules, flat_rules);
}
rewriteString(string, flat_rules)
}
}
Variant: PenultimateSquaring, based on exponentiation by squaring,
needs only \(\mathcal{O}(\log(d))\) rewrites.
More graphs in the report
Thanks to
Jiří Kosinka for always being available to answer my weird questions