[gnumeric] Derivatives: simplify expressions further.



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]