[gnumeric] Derivatives: simplify expressions further.
- From: Morten Welinder <mortenw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnumeric] Derivatives: simplify expressions further.
- Date: Sun, 20 May 2018 17:09:29 +0000 (UTC)
commit 84d7e60cedc3dcdf2b6ebc2038db5244b75c0d34
Author: Morten Welinder <terra gnome org>
Date: Sun May 20 13:07:54 2018 -0400
Derivatives: simplify expressions further.
Also add DERIV function, but only during the test suite.
plugins/fn-math/functions.c | 2 +-
src/expr-deriv.c | 140 ++++++++++++++++++++++++++++++++++++++++++-
src/expr-deriv.h | 3 +-
src/func-builtin.c | 51 ++++++++++++++-
4 files changed, 188 insertions(+), 8 deletions(-)
---
diff --git a/plugins/fn-math/functions.c b/plugins/fn-math/functions.c
index 748a70f..a092e1f 100644
--- a/plugins/fn-math/functions.c
+++ b/plugins/fn-math/functions.c
@@ -3744,7 +3744,7 @@ go_plugin_init (GOPlugin *plugin, GOCmdContext *cc)
{
gnm_expr_deriv_install_handler (gnm_func_lookup ("sumsq", NULL),
gnumeric_sumsq_deriv,
- GNM_EXPR_DERIV_NO_CHAIN,
+ GNM_EXPR_DERIV_NO_CHAIN | GNM_EXPR_DERIV_OPTIMIZE,
NULL, NULL);
gnm_expr_deriv_install_handler (gnm_func_lookup ("exp", NULL),
gnumeric_exp_deriv,
diff --git a/src/expr-deriv.c b/src/expr-deriv.c
index f86d090..6654a47 100644
--- a/src/expr-deriv.c
+++ b/src/expr-deriv.c
@@ -130,12 +130,22 @@ gnm_value_deriv (GnmValue const *v)
return NULL;
}
+static GnmExpr const *madd (GnmExpr const *l, gboolean copyl, GnmExpr const *r, gboolean copyr);
+static GnmExpr const *msub (GnmExpr const *l, gboolean copyl, GnmExpr const *r, gboolean copyr);
+static GnmExpr const *mmul (GnmExpr const *l, gboolean copyl, GnmExpr const *r, gboolean copyr);
+static GnmExpr const *mdiv (GnmExpr const *l, gboolean copyl, GnmExpr const *r, gboolean copyr);
+static GnmExpr const *mneg (GnmExpr const *l, gboolean copyl);
+static GnmExpr const *optimize_sum (GnmExpr const *e);
+
+
+
+
static gboolean
is_any_const (GnmExpr const *e, gnm_float *c)
{
GnmValue const *v = gnm_expr_get_constant (e);
if (v && VALUE_IS_FLOAT (v)) {
- *c = value_get_as_float (v);
+ if (c) *c = value_get_as_float (v);
return TRUE;
} else
return FALSE;
@@ -148,6 +158,20 @@ is_const (GnmExpr const *e, gnm_float c)
return v && VALUE_IS_FLOAT (v) && value_get_as_float (v) == c;
}
+static gboolean
+is_neg (GnmExpr const *e)
+{
+ return (GNM_EXPR_GET_OPER (e) == GNM_EXPR_OP_UNARY_NEG);
+}
+
+static gboolean
+is_lcmul (GnmExpr const *e, gnm_float *c)
+{
+ return (GNM_EXPR_GET_OPER (e) == GNM_EXPR_OP_MULT &&
+ is_any_const (e->binary.value_a, c));
+}
+
+
// Optimizing constructor for "+". Takes ownership of "l" and "r"
// if the corresponding "copy" argument is false.
//
@@ -187,6 +211,13 @@ mneg (GnmExpr const *l, gboolean copyl)
return gnm_expr_new_constant (value_new_float (-x));
}
+ if (is_lcmul (l, &x)) {
+ GnmExpr const *res = mmul (gnm_expr_new_constant (value_new_float (-x)), 0,
+ l->binary.value_b, 1);
+ if (!copyl) gnm_expr_free (l);
+ return res;
+ }
+
if (copyl) l = gnm_expr_copy (l);
return gnm_expr_new_unary (GNM_EXPR_OP_UNARY_NEG, l);
}
@@ -240,6 +271,26 @@ mmul (GnmExpr const *l, gboolean copyl, GnmExpr const *r, gboolean copyr)
return mneg (r, copyr);
}
+ if (is_neg (l)) {
+ GnmExpr const *res = mneg (mmul (l->unary.value, 1, r, copyr), 0);
+ if (!copyl) gnm_expr_free (l);
+ return res;
+ }
+
+ if (is_neg (r)) {
+ GnmExpr const *res = mneg (mmul (l, copyl, r->unary.value, 1), 0);
+ if (!copyr) gnm_expr_free (r);
+ return res;
+ }
+
+ if (is_lcmul (l, NULL)) {
+ GnmExpr const *res = mmul (l->binary.value_a, 1,
+ mmul (l->binary.value_b, 1,
+ r, copyr), 0);
+ if (!copyl) gnm_expr_free (l);
+ return res;
+ }
+
if (copyl) l = gnm_expr_copy (l);
if (copyr) r = gnm_expr_copy (r);
return gnm_expr_new_binary (l, GNM_EXPR_OP_MULT, r);
@@ -283,6 +334,83 @@ mexp (GnmExpr const *l, gboolean copyl, GnmExpr const *r, gboolean copyr)
return gnm_expr_new_binary (l, GNM_EXPR_OP_EXP, r);
}
+static GnmExpr const *
+msum (GnmExprList *as)
+{
+ GnmFunc *fsum = gnm_func_lookup_or_add_placeholder ("SUM");
+ GnmExpr const *res = gnm_expr_new_funcall (fsum, as);
+ GnmExpr const *opt = optimize_sum (res);
+
+ if (opt) {
+ gnm_expr_free (res);
+ res = opt;
+ }
+
+ return res;
+}
+
+static GnmExpr const *
+optimize_sum (GnmExpr const *e)
+{
+ int argc = e->func.argc;
+ GnmExprConstPtr *argv = e->func.argv;
+ gboolean all_neg = (argc > 0);
+ gboolean all_lcmul = (argc > 0);
+ gnm_float cl = 0;
+ int i;
+
+ for (i = 0; i < argc; i++) {
+ GnmExpr const *a = argv[i];
+ gnm_float x;
+
+ all_neg = all_neg && is_neg (a);
+
+ all_lcmul = all_lcmul &&
+ is_lcmul (a, &x) &&
+ ((i == 0) ? ((cl = x), TRUE) : (cl == x));
+ }
+
+ if (all_neg) {
+ GnmExprList *as = NULL;
+ for (i = argc; i-- > 0;) {
+ GnmExpr const *a = argv[i];
+ as = g_slist_prepend (as, (gpointer)gnm_expr_copy (a->unary.value));
+ }
+ return mneg (msum (as), 0);
+ }
+
+ if (all_lcmul) {
+ GnmExprList *as = NULL;
+ for (i = argc; i-- > 0;) {
+ GnmExpr const *a = argv[i];
+ as = g_slist_prepend (as, (gpointer)gnm_expr_copy (a->binary.value_b));
+ }
+ return mmul (gnm_expr_new_constant (value_new_float (cl)), 0,
+ msum (as), 0);
+ }
+
+ return NULL;
+}
+
+static GnmExpr const *
+optimize (GnmExpr const *e)
+{
+ GnmExprOp op = GNM_EXPR_GET_OPER (e);
+
+ switch (op) {
+ case GNM_EXPR_OP_FUNCALL: {
+ GnmFunc *f = gnm_expr_get_func_def (e);
+ GnmFunc *fsum = gnm_func_lookup_or_add_placeholder ("SUM");
+
+ if (f == fsum)
+ return optimize_sum (e);
+ return NULL;
+ }
+ default:
+ return NULL;
+ }
+}
+
/* ------------------------------------------------------------------------- */
struct cb_arg_collect {
@@ -476,6 +604,14 @@ gnm_expr_deriv (GnmExpr const *expr,
res = mmul (res, 0, e2, 0);
}
+ if (di->flags & GNM_EXPR_DERIV_OPTIMIZE) {
+ GnmExpr const *opt = optimize (res);
+ if (opt) {
+ gnm_expr_free (res);
+ res = opt;
+ }
+ }
+
return res;
}
@@ -659,7 +795,7 @@ gnm_expr_cell_deriv_value (GnmCell *y, GnmCell *x)
* gnm_expr_deriv_install_handler:
* @func: the function being given a handler
* @h: (scope notified): #GnmExprDerivHandler
- * @flags:
+ * @flags:
* @data: user data for @h
* @notify: destroy notification for @data
*/
diff --git a/src/expr-deriv.h b/src/expr-deriv.h
index f9a01bd..0b9c0c9 100644
--- a/src/expr-deriv.h
+++ b/src/expr-deriv.h
@@ -45,7 +45,8 @@ typedef GnmExpr const * (*GnmExprDerivHandler) (GnmExpr const *expr,
gpointer user);
typedef enum {
GNM_EXPR_DERIV_NO_CHAIN = 0x0,
- GNM_EXPR_DERIV_CHAIN = 0x1
+ GNM_EXPR_DERIV_CHAIN = 0x1,
+ GNM_EXPR_DERIV_OPTIMIZE = 0x2
} GnmExprDerivFlags;
void gnm_expr_deriv_install_handler (GnmFunc *func, GnmExprDerivHandler h,
diff --git a/src/func-builtin.c b/src/func-builtin.c
index 862110f..c32800c 100644
--- a/src/func-builtin.c
+++ b/src/func-builtin.c
@@ -34,6 +34,7 @@
#include <application.h>
#include <number-match.h>
#include <gutils.h>
+#include <ranges.h>
/***************************************************************************/
@@ -450,6 +451,40 @@ gnumeric_number_match (GnmFuncEvalInfo *ei, GnmValue const * const *args)
/***************************************************************************/
+static GnmFuncHelp const help_deriv[] = {
+ /* Not for public consumption. */
+ { GNM_FUNC_HELP_END }
+};
+
+static GnmValue *
+gnumeric_deriv (GnmFuncEvalInfo *ei, GnmValue const * const *args)
+{
+ GnmValue const *vy = args[0];
+ GnmValue const *vx = args[1];
+ Sheet *sy, *sy2, *sx, *sx2;
+ GnmRange ry, rx;
+ GnmCell *cy, *cx;
+
+ if (!VALUE_IS_CELLRANGE (vy) ||
+ !VALUE_IS_CELLRANGE (vx))
+ return value_new_error_VALUE (ei->pos);
+
+ gnm_rangeref_normalize (value_get_rangeref (vy), ei->pos, &sy, &sy2, &ry);
+ gnm_rangeref_normalize (value_get_rangeref (vx), ei->pos, &sx, &sx2, &rx);
+ if (!range_is_singleton (&ry) || sy2 != sy ||
+ !range_is_singleton (&rx) || sx2 != sx)
+ return value_new_error_VALUE (ei->pos);
+
+ cy = sheet_cell_get (sy, ry.start.col, ry.start.row);
+ cx = sheet_cell_get (sx, rx.start.col, rx.start.row);
+ if (!cy || !cx)
+ return value_new_error_VALUE (ei->pos);
+
+ return value_new_float (gnm_expr_cell_deriv_value (cy, cx));
+}
+
+/***************************************************************************/
+
static GnmFuncGroup *math_group = NULL;
static GnmFuncGroup *gnumeric_group = NULL;
static GnmFuncGroup *logic_group = NULL;
@@ -489,12 +524,18 @@ func_builtin_init (void)
GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC,
GNM_FUNC_TEST_STATUS_EXHAUSTIVE
},
- { "number_match", "s|s",
+ { "number_match", "s|s", // Only in test suite
help_number_match, gnumeric_number_match, NULL,
NULL, NULL,
GNM_FUNC_SIMPLE,
GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC,
GNM_FUNC_TEST_STATUS_BASIC },
+ { "deriv", "r|r", // Only in test suite
+ help_deriv, gnumeric_deriv, NULL,
+ NULL, NULL,
+ GNM_FUNC_SIMPLE,
+ GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC,
+ GNM_FUNC_TEST_STATUS_BASIC },
/* --- Logic --- */
{ "if", "b|EE",
help_if, gnumeric_if, NULL,
@@ -514,9 +555,11 @@ func_builtin_init (void)
gnumeric_group = gnm_func_group_fetch (gname, _(gname));
gnm_func_add (gnumeric_group, builtins + i++, tdomain);
gnm_func_add (gnumeric_group, builtins + i++, tdomain);
- if (gnm_debug_flag ("testsuite"))
+ if (gnm_debug_flag ("testsuite")) {
gnm_func_add (gnumeric_group, builtins + i, tdomain);
- i++;
+ gnm_func_add (gnumeric_group, builtins + i + 1, tdomain);
+ }
+ i += 2;
gname = N_("Logic");
logic_group = gnm_func_group_fetch (gname, _(gname));
@@ -524,7 +567,7 @@ func_builtin_init (void)
gnm_expr_deriv_install_handler (gnm_func_lookup ("sum", NULL),
gnumeric_sum_deriv,
- GNM_EXPR_DERIV_NO_CHAIN,
+ GNM_EXPR_DERIV_NO_CHAIN | GNM_EXPR_DERIV_OPTIMIZE,
NULL, NULL);
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]