From e34de1b33b5e0d351b15921446b60d3b5fc7730a Mon Sep 17 00:00:00 2001 From: ChickChicky Date: Wed, 6 Nov 2024 12:59:45 +0100 Subject: [PATCH 1/6] Added function optimization --- randomart.c | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/randomart.c b/randomart.c index 48ce87a..096a825 100644 --- a/randomart.c +++ b/randomart.c @@ -505,6 +505,94 @@ Node *gen_rule(Grammar grammar, Arena *arena, size_t rule, int depth) return node; } +bool get_number(Node *node, float *result) { + if (node->kind != NK_NUMBER) + return false; + if (result) + *result = node->as.number; + return true; +} + +bool get_boolean(Node *node, bool *result) { + if (node->kind != NK_BOOLEAN) + return false; + if (result) + *result = node->as.boolean; + return true; +} + +#define node_number_inline(value) (Node){ .kind = NK_NUMBER, .file = __FILE__, .line = __LINE__, .as.number = (value) } +#define node_boolean_inline(value) (Node){ .kind = NK_BOOLEAN, .file = __FILE__, .line = __LINE__, .as.boolean = (value) } + +// TODO: Probably try to free the discarded nodes +bool optimize_expr(Arena *arena, Node *expr) { + switch (expr->kind) { + case NK_X: + case NK_Y: + case NK_BOOLEAN: + case NK_NUMBER: return false; + case NK_RANDOM: + case NK_RULE: { + printf("%s:%d: ERROR: cannot optimize a node that valid only for grammar definitions\n", expr->file, expr->line); + return false; + } + case NK_ADD: { + bool res = optimize_expr(arena, expr->as.binop.lhs) || optimize_expr(arena, expr->as.binop.rhs); + float lhs, rhs; + if (!(get_number(expr->as.binop.lhs, &lhs) && get_number(expr->as.binop.rhs, &rhs))) + return res; + *expr = node_number_inline(lhs+rhs); + return true; + } + case NK_MULT: { + bool res = optimize_expr(arena, expr->as.binop.lhs) || optimize_expr(arena, expr->as.binop.rhs); + float lhs, rhs; + if (!(get_number(expr->as.binop.lhs, &lhs) && get_number(expr->as.binop.rhs, &rhs))) + return res; + *expr = node_number_inline(lhs*rhs); + return true; + } + case NK_MOD: { + bool res = optimize_expr(arena, expr->as.binop.lhs) || optimize_expr(arena, expr->as.binop.rhs); + float lhs, rhs; + if (!(get_number(expr->as.binop.lhs, &lhs) && get_number(expr->as.binop.rhs, &rhs))) + return res; + *expr = node_number_inline(fmodf(lhs,rhs)); + return true; + } + case NK_GT: { + bool res = optimize_expr(arena, expr->as.binop.lhs) || optimize_expr(arena, expr->as.binop.rhs); + float lhs, rhs; + if (!(get_number(expr->as.binop.lhs, &lhs) && get_number(expr->as.binop.rhs, &rhs))) + return res; + *expr = node_boolean_inline(lhs>rhs); + return true; + } + case NK_TRIPLE: { + return + optimize_expr(arena, expr->as.triple.first) || + optimize_expr(arena, expr->as.triple.second) || + optimize_expr(arena, expr->as.triple.third) + ; + } + case NK_IF: { + bool res = optimize_expr(arena, expr->as.iff.then) || optimize_expr(arena, expr->as.iff.elze) || optimize_expr(arena, expr->as.iff.cond); + float then, elze; + bool cond; + if (!(get_number(expr->as.iff.then, &then) && get_number(expr->as.iff.elze, &elze) && get_boolean(expr->as.iff.cond, &cond))) + return res; + *expr = cond ? node_number_inline(then) : node_number_inline(elze); + return true; + } + case COUNT_NK: + default: UNREACHABLE("optimize_expr"); + } +} + +void optimize_func(Arena *arena, Node *func) { + while (optimize_expr(arena, func)); +} + size_t arch[] = {2, 28, 28, 9, 3}; int main() @@ -561,6 +649,7 @@ int main() fprintf(stderr, "ERROR: the crappy generation process could not terminate\n"); return 1; } + optimize_func(&static_arena, f); node_print_ln(f); // bool ok = render_pixels(node_triple(node_x(), node_x(), node_x())); From 93c9e09b32921ee5a7eaeac42ed29d79a87b36f0 Mon Sep 17 00:00:00 2001 From: ChickChicky Date: Wed, 6 Nov 2024 14:57:33 +0100 Subject: [PATCH 2/6] Small improvements to optimizer * Added optimization passes limit * Slightly modified formatting --- randomart.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/randomart.c b/randomart.c index 096a825..4922bb8 100644 --- a/randomart.c +++ b/randomart.c @@ -107,6 +107,7 @@ Node *node_number_loc(const char *file, int line, Arena *arena, float number) return node; } #define node_number(arena, number) node_number_loc(__FILE__, __LINE__, arena, number) +#define node_number_inline(value) (Node){ .kind = NK_NUMBER, .file = __FILE__, .line = __LINE__, .as.number = (value) } Node *node_rule_loc(const char *file, int line, Arena *arena, int rule) { @@ -123,6 +124,7 @@ Node *node_boolean_loc(const char *file, int line, Arena *arena, bool boolean) return node; } #define node_boolean(arena, boolean) node_boolean_loc(__FILE__, __LINE__, arena, boolean) +#define node_boolean_inline(value) (Node){ .kind = NK_BOOLEAN, .file = __FILE__, .line = __LINE__, .as.boolean = (value) } #define node_x(arena) node_loc(__FILE__, __LINE__, arena, NK_X) #define node_y(arena) node_loc(__FILE__, __LINE__, arena, NK_Y) @@ -505,7 +507,8 @@ Node *gen_rule(Grammar grammar, Arena *arena, size_t rule, int depth) return node; } -bool get_number(Node *node, float *result) { +bool get_number(Node *node, float *result) +{ if (node->kind != NK_NUMBER) return false; if (result) @@ -513,7 +516,8 @@ bool get_number(Node *node, float *result) { return true; } -bool get_boolean(Node *node, bool *result) { +bool get_boolean(Node *node, bool *result) +{ if (node->kind != NK_BOOLEAN) return false; if (result) @@ -521,11 +525,11 @@ bool get_boolean(Node *node, bool *result) { return true; } -#define node_number_inline(value) (Node){ .kind = NK_NUMBER, .file = __FILE__, .line = __LINE__, .as.number = (value) } -#define node_boolean_inline(value) (Node){ .kind = NK_BOOLEAN, .file = __FILE__, .line = __LINE__, .as.boolean = (value) } +#define OPTIMIZE_MAX_PASSES 100 // TODO: Probably try to free the discarded nodes -bool optimize_expr(Arena *arena, Node *expr) { +bool optimize_expr(Arena *arena, Node *expr) +{ switch (expr->kind) { case NK_X: case NK_Y: @@ -589,8 +593,13 @@ bool optimize_expr(Arena *arena, Node *expr) { } } -void optimize_func(Arena *arena, Node *func) { - while (optimize_expr(arena, func)); +bool optimize_func(Arena *arena, Node *func) +{ + for (size_t i = 0; i < OPTIMIZE_MAX_PASSES; i++) { + if (!optimize_expr(arena, func)) + return true; + } + return false; } size_t arch[] = {2, 28, 28, 9, 3}; @@ -649,7 +658,9 @@ int main() fprintf(stderr, "ERROR: the crappy generation process could not terminate\n"); return 1; } - optimize_func(&static_arena, f); + if (!optimize_func(&static_arena, f)) { + nob_log(WARNING, "Exceeded maximum optimization passes"); + } node_print_ln(f); // bool ok = render_pixels(node_triple(node_x(), node_x(), node_x())); From 300e7d1e02dec18ace7e46fdf4ce1a3e5b2a9b1b Mon Sep 17 00:00:00 2001 From: ChickChicky Date: Thu, 7 Nov 2024 07:09:14 +0100 Subject: [PATCH 3/6] Tweaks to optimization * Disabled optimization if OPTIMIZE_MAX_PASSES <= 0 * Unknown/invalid nodes are ignored instead of causing an error --- randomart.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/randomart.c b/randomart.c index 4922bb8..1f7b65f 100644 --- a/randomart.c +++ b/randomart.c @@ -527,7 +527,6 @@ bool get_boolean(Node *node, bool *result) #define OPTIMIZE_MAX_PASSES 100 -// TODO: Probably try to free the discarded nodes bool optimize_expr(Arena *arena, Node *expr) { switch (expr->kind) { @@ -535,11 +534,6 @@ bool optimize_expr(Arena *arena, Node *expr) case NK_Y: case NK_BOOLEAN: case NK_NUMBER: return false; - case NK_RANDOM: - case NK_RULE: { - printf("%s:%d: ERROR: cannot optimize a node that valid only for grammar definitions\n", expr->file, expr->line); - return false; - } case NK_ADD: { bool res = optimize_expr(arena, expr->as.binop.lhs) || optimize_expr(arena, expr->as.binop.rhs); float lhs, rhs; @@ -588,13 +582,16 @@ bool optimize_expr(Arena *arena, Node *expr) *expr = cond ? node_number_inline(then) : node_number_inline(elze); return true; } - case COUNT_NK: - default: UNREACHABLE("optimize_expr"); + default: + return false; } } bool optimize_func(Arena *arena, Node *func) { +#if OPTIMIZE_MAX_PASSES <= 0 + return true; +#endif for (size_t i = 0; i < OPTIMIZE_MAX_PASSES; i++) { if (!optimize_expr(arena, func)) return true; From 48af72e7423c0caab175c0308877dffd57a2c804 Mon Sep 17 00:00:00 2001 From: ChickChicky Date: Thu, 7 Nov 2024 07:12:12 +0100 Subject: [PATCH 4/6] Fix stoopid mistake with optimize_expr --- randomart.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/randomart.c b/randomart.c index 1f7b65f..205fae3 100644 --- a/randomart.c +++ b/randomart.c @@ -533,7 +533,10 @@ bool optimize_expr(Arena *arena, Node *expr) case NK_X: case NK_Y: case NK_BOOLEAN: - case NK_NUMBER: return false; + case NK_NUMBER: + case NK_RANDOM: + case NK_RULE: + return false; case NK_ADD: { bool res = optimize_expr(arena, expr->as.binop.lhs) || optimize_expr(arena, expr->as.binop.rhs); float lhs, rhs; @@ -582,8 +585,9 @@ bool optimize_expr(Arena *arena, Node *expr) *expr = cond ? node_number_inline(then) : node_number_inline(elze); return true; } + case COUNT_NK: default: - return false; + UNREACHABLE("optimize_expr"); } } From 960e2a1c09b3ee569836a7ecc4ce504ccfc14411 Mon Sep 17 00:00:00 2001 From: ChickChicky Date: Thu, 7 Nov 2024 12:46:15 +0100 Subject: [PATCH 5/6] Small fix to suppress warnings * Moved the actual `optimize_func` body into else clause --- randomart.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/randomart.c b/randomart.c index 205fae3..4251edf 100644 --- a/randomart.c +++ b/randomart.c @@ -595,12 +595,13 @@ bool optimize_func(Arena *arena, Node *func) { #if OPTIMIZE_MAX_PASSES <= 0 return true; -#endif +#else for (size_t i = 0; i < OPTIMIZE_MAX_PASSES; i++) { if (!optimize_expr(arena, func)) return true; } return false; +#endif } size_t arch[] = {2, 28, 28, 9, 3}; From a313e276d07d434ef5966fff2fd3de56310e05d6 Mon Sep 17 00:00:00 2001 From: ChickChicky Date: Thu, 7 Nov 2024 17:57:53 +0100 Subject: [PATCH 6/6] Refactored optimization * Removed `MAX_PASSES`, as it is recursive * Removed `optimize_func` as such * Fix 'if' node only working with numeric 'then'/'elze' * Added check for trivial 'gt' with x/y and -1/1 * Removed `arena` parameter, as it is unused --- randomart.c | 102 +++++++++++++++++++++------------------------------- 1 file changed, 41 insertions(+), 61 deletions(-) diff --git a/randomart.c b/randomart.c index 4251edf..2521a6d 100644 --- a/randomart.c +++ b/randomart.c @@ -525,10 +525,9 @@ bool get_boolean(Node *node, bool *result) return true; } -#define OPTIMIZE_MAX_PASSES 100 - -bool optimize_expr(Arena *arena, Node *expr) +void optimize_expr(Node *expr) { + float lhs, rhs; switch (expr->kind) { case NK_X: case NK_Y: @@ -536,74 +535,57 @@ bool optimize_expr(Arena *arena, Node *expr) case NK_NUMBER: case NK_RANDOM: case NK_RULE: - return false; + break; case NK_ADD: { - bool res = optimize_expr(arena, expr->as.binop.lhs) || optimize_expr(arena, expr->as.binop.rhs); - float lhs, rhs; - if (!(get_number(expr->as.binop.lhs, &lhs) && get_number(expr->as.binop.rhs, &rhs))) - return res; - *expr = node_number_inline(lhs+rhs); - return true; - } + optimize_expr(expr->as.binop.lhs); + optimize_expr(expr->as.binop.rhs); + if (get_number(expr->as.binop.lhs, &lhs) && get_number(expr->as.binop.rhs, &rhs)) + *expr = node_number_inline(lhs+rhs); + } break; case NK_MULT: { - bool res = optimize_expr(arena, expr->as.binop.lhs) || optimize_expr(arena, expr->as.binop.rhs); - float lhs, rhs; - if (!(get_number(expr->as.binop.lhs, &lhs) && get_number(expr->as.binop.rhs, &rhs))) - return res; - *expr = node_number_inline(lhs*rhs); - return true; - } + optimize_expr(expr->as.binop.lhs); + optimize_expr(expr->as.binop.rhs); + if (get_number(expr->as.binop.lhs, &lhs) && get_number(expr->as.binop.rhs, &rhs)) + *expr = node_number_inline(lhs*rhs); + } break; case NK_MOD: { - bool res = optimize_expr(arena, expr->as.binop.lhs) || optimize_expr(arena, expr->as.binop.rhs); - float lhs, rhs; - if (!(get_number(expr->as.binop.lhs, &lhs) && get_number(expr->as.binop.rhs, &rhs))) - return res; - *expr = node_number_inline(fmodf(lhs,rhs)); - return true; - } + optimize_expr(expr->as.binop.lhs); + optimize_expr(expr->as.binop.rhs); + if (get_number(expr->as.binop.lhs, &lhs) && get_number(expr->as.binop.rhs, &rhs)) + *expr = node_number_inline(fmodf(lhs,rhs)); + } break; case NK_GT: { - bool res = optimize_expr(arena, expr->as.binop.lhs) || optimize_expr(arena, expr->as.binop.rhs); - float lhs, rhs; - if (!(get_number(expr->as.binop.lhs, &lhs) && get_number(expr->as.binop.rhs, &rhs))) - return res; - *expr = node_boolean_inline(lhs>rhs); - return true; - } + optimize_expr(expr->as.binop.lhs); + optimize_expr(expr->as.binop.rhs); + bool num_rhs = get_number(expr->as.binop.rhs, &rhs); + bool num_lhs = get_number(expr->as.binop.lhs, &lhs); + if (num_rhs && num_lhs) + *expr = node_boolean_inline(lhs>rhs); + // maybe too much of an edge case + else if ((expr->as.binop.lhs->kind == NK_X || expr->as.binop.lhs->kind == NK_Y) && num_rhs && rhs >= 1.f) + *expr = node_boolean_inline(false); + else if ((expr->as.binop.rhs->kind == NK_X || expr->as.binop.rhs->kind == NK_Y) && num_lhs && lhs <= -1.f) + *expr = node_boolean_inline(false); + } break; case NK_TRIPLE: { - return - optimize_expr(arena, expr->as.triple.first) || - optimize_expr(arena, expr->as.triple.second) || - optimize_expr(arena, expr->as.triple.third) - ; - } + optimize_expr(expr->as.triple.first); + optimize_expr(expr->as.triple.second); + optimize_expr(expr->as.triple.third); + } break; case NK_IF: { - bool res = optimize_expr(arena, expr->as.iff.then) || optimize_expr(arena, expr->as.iff.elze) || optimize_expr(arena, expr->as.iff.cond); - float then, elze; + optimize_expr(expr->as.iff.cond); + optimize_expr(expr->as.iff.then); + optimize_expr(expr->as.iff.elze); bool cond; - if (!(get_number(expr->as.iff.then, &then) && get_number(expr->as.iff.elze, &elze) && get_boolean(expr->as.iff.cond, &cond))) - return res; - *expr = cond ? node_number_inline(then) : node_number_inline(elze); - return true; - } + if (get_boolean(expr->as.iff.cond, &cond)) + *expr = cond ? *expr->as.iff.then : *expr->as.iff.elze; + } break; case COUNT_NK: default: UNREACHABLE("optimize_expr"); } } -bool optimize_func(Arena *arena, Node *func) -{ -#if OPTIMIZE_MAX_PASSES <= 0 - return true; -#else - for (size_t i = 0; i < OPTIMIZE_MAX_PASSES; i++) { - if (!optimize_expr(arena, func)) - return true; - } - return false; -#endif -} - size_t arch[] = {2, 28, 28, 9, 3}; int main() @@ -660,9 +642,7 @@ int main() fprintf(stderr, "ERROR: the crappy generation process could not terminate\n"); return 1; } - if (!optimize_func(&static_arena, f)) { - nob_log(WARNING, "Exceeded maximum optimization passes"); - } + optimize_expr(f); node_print_ln(f); // bool ok = render_pixels(node_triple(node_x(), node_x(), node_x()));