[gnumeric] Deriv: move function derivation to where functions are defined.



commit c298bb4c35d33b20d102c97a2926e9f385de64e0
Author: Morten Welinder <terra gnome org>
Date:   Tue Sep 13 19:39:08 2016 -0400

    Deriv: move function derivation to where functions are defined.
    
    Make it so new handlers can be installed as needed.

 plugins/fn-math/functions.c |   50 +++++++++++
 src/expr-deriv.c            |  192 ++++++++++++++++++++++++-------------------
 src/expr-deriv.h            |   24 ++++++
 src/func-builtin.c          |   35 ++++++++
 src/libgnumeric.c           |    2 +
 5 files changed, 220 insertions(+), 83 deletions(-)
---
diff --git a/plugins/fn-math/functions.c b/plugins/fn-math/functions.c
index 56b81c1..1b3401c 100644
--- a/plugins/fn-math/functions.c
+++ b/plugins/fn-math/functions.c
@@ -35,6 +35,7 @@
 #include <value.h>
 #include <criteria.h>
 #include <expr.h>
+#include <expr-deriv.h>
 #include <position.h>
 #include <regression.h>
 #include <gnm-i18n.h>
@@ -968,6 +969,13 @@ gnumeric_exp (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
        return value_new_float (gnm_exp (value_get_as_float (argv[0])));
 }
 
+static GnmExpr const *
+gnumeric_exp_deriv (GnmExpr const *expr, GnmEvalPos const *ep,
+                     GnmExprDeriv *info)
+{
+       return gnm_expr_copy (expr);
+}
+
 /***************************************************************************/
 
 static GnmFuncHelp const help_expm1[] = {
@@ -1704,6 +1712,31 @@ gnumeric_sumsq (GnmFuncEvalInfo *ei, int argc, GnmExprConstPtr const *argv)
                                     GNM_ERROR_VALUE);
 }
 
+static GnmExpr const *
+gnumeric_sumsq_deriv (GnmExpr const *expr, GnmEvalPos const *ep,
+                     GnmExprDeriv *info)
+{
+       GnmExprList *l, *args = gnm_expr_deriv_collect (expr, ep, info);
+       GnmExpr const *res;
+       GnmExpr const *sqsum;
+       GnmFunc *fsum = gnm_func_lookup_or_add_placeholder ("SUM");
+
+       for (l = args; l; l = l->next) {
+               GnmExpr const *e = l->data;
+               GnmExpr const *ee = gnm_expr_new_binary
+                       (e,
+                        GNM_EXPR_OP_EXP,
+                        gnm_expr_new_constant (value_new_int (2)));
+               l->data = (gpointer)ee;
+       }
+
+       sqsum = gnm_expr_new_funcall (fsum, args);
+       res = gnm_expr_deriv (sqsum, ep, info);
+       gnm_expr_free (sqsum);
+
+       return res;
+}
+
 /***************************************************************************/
 
 static GnmFuncHelp const help_multinomial[] = {
@@ -3660,3 +3693,20 @@ GnmFuncDescriptor const math_functions[] = {
 #endif
        {NULL}
 };
+
+G_MODULE_EXPORT void
+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_install_handler (gnm_func_lookup ("exp", NULL),
+                                       gnumeric_exp_deriv,
+                                       GNM_EXPR_DERIV_CHAIN);
+}
+
+G_MODULE_EXPORT void
+go_plugin_shutdown (GOPlugin *plugin, GOCmdContext *cc)
+{
+       gnm_expr_deriv_uninstall_handler (gnm_func_lookup ("sumsq", NULL));
+}
diff --git a/src/expr-deriv.c b/src/expr-deriv.c
index 90d56f1..47bf419 100644
--- a/src/expr-deriv.c
+++ b/src/expr-deriv.c
@@ -32,6 +32,15 @@
 
 /* ------------------------------------------------------------------------- */
 
+static GHashTable *deriv_handlers;
+
+struct DerivInfo {
+       GnmExprDerivHandler handler;
+       GnmExprDerivFlags flags;
+};
+
+/* ------------------------------------------------------------------------- */
+
 struct GnmExprDeriv_ {
        GnmEvalPos var;
 };
@@ -48,7 +57,6 @@ gnm_expr_deriv_info_free (GnmExprDeriv *deriv)
        g_free (deriv);
 }
 
-
 void
 gnm_expr_deriv_info_set_var (GnmExprDeriv *deriv, GnmEvalPos const *var)
 {
@@ -67,6 +75,17 @@ gnm_value_deriv (GnmValue const *v)
 }
 
 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);
+               return TRUE;
+       } else
+               return FALSE;
+}
+
+static gboolean
 is_const (GnmExpr const *e, gnm_float c)
 {
        GnmValue const *v = gnm_expr_get_constant (e);
@@ -106,6 +125,12 @@ madd (GnmExpr const *l, gboolean copyl, GnmExpr const *r, gboolean copyr)
 static GnmExpr const *
 mneg (GnmExpr const *l, gboolean copyl)
 {
+       gnm_float x;
+       if (is_any_const (l, &x)) {
+               if (!copyl) gnm_expr_free (l);
+               return gnm_expr_new_constant (value_new_float (-x));
+       }
+
        if (copyl) l = gnm_expr_copy (l);
        return gnm_expr_new_unary (GNM_EXPR_OP_UNARY_NEG, l);
 }
@@ -154,6 +179,11 @@ mmul (GnmExpr const *l, gboolean copyl, GnmExpr const *r, gboolean copyr)
                return l;
        }
 
+       if (is_const (l, -1)) {
+               if (!copyl) gnm_expr_free (l);
+               return mneg (r, copyr);
+       }
+
        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);
@@ -197,11 +227,6 @@ 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 *
-gnm_expr_deriv (GnmExpr const *expr,
-               GnmEvalPos const *ep,
-               GnmExprDeriv *info);
-
 /* ------------------------------------------------------------------------- */
 
 struct cb_arg_collect {
@@ -230,7 +255,7 @@ cb_arg_collect (GnmCellIter const *iter, gpointer user_)
 }
 
 // Collect all arguments and expand range arguments into individual cells.
-static GnmExprList *
+GnmExprList *
 gnm_expr_deriv_collect (GnmExpr const *expr,
                        GnmEvalPos const *ep,
                        GnmExprDeriv *info)
@@ -260,63 +285,6 @@ gnm_expr_deriv_collect (GnmExpr const *expr,
        return user.args;
 }
 
-
-static GnmExpr const *
-gnm_expr_deriv_sum (GnmExpr const *expr,
-                   GnmEvalPos const *ep,
-                   GnmExprDeriv *info)
-{
-       GnmExprList *l, *args = gnm_expr_deriv_collect (expr, ep, info);
-       GnmFunc *fsum = gnm_func_lookup_or_add_placeholder ("SUM");
-       gboolean bad = FALSE;
-
-       for (l = args; l; l = l->next) {
-               GnmExpr const *e = l->data;
-               GnmExpr const *d = gnm_expr_deriv (e, ep, info);
-               if (d) {
-                       gnm_expr_free (e);
-                       l->data = (gpointer)d;
-               } else {
-                       bad = TRUE;
-                       break;
-               }
-       }
-
-       if (bad) {
-               for (l = args; l; l = l->next)
-                       gnm_expr_free (l->data);
-               gnm_expr_list_free (args);
-               return NULL;
-       } else
-               return gnm_expr_new_funcall (fsum, args);
-}
-
-static GnmExpr const *
-gnm_expr_deriv_sumsq (GnmExpr const *expr,
-                     GnmEvalPos const *ep,
-                     GnmExprDeriv *info)
-{
-       GnmExprList *l, *args = gnm_expr_deriv_collect (expr, ep, info);
-       GnmExpr const *res;
-       GnmExpr const *sqsum;
-       GnmFunc *fsum = gnm_func_lookup_or_add_placeholder ("SUM");
-
-       for (l = args; l; l = l->next) {
-               GnmExpr const *e = l->data;
-               GnmExpr const *ee = gnm_expr_new_binary
-                       (e,
-                        GNM_EXPR_OP_EXP,
-                        gnm_expr_new_constant (value_new_int (2)));
-               l->data = (gpointer)ee;
-       }
-
-       sqsum = gnm_expr_new_funcall (fsum, args);
-       res = gnm_expr_deriv (sqsum, ep, info);
-       gnm_expr_free (sqsum);
-
-       return res;
-}
-
 /* ------------------------------------------------------------------------- */
 
 #define MAYBE_FREE(e) do { if (e) gnm_expr_free (e); } while (0)
@@ -335,7 +303,7 @@ gnm_expr_deriv_sumsq (GnmExpr const *expr,
 #define COMMON_BINARY_END }
 
 
-static GnmExpr const *
+GnmExpr const *
 gnm_expr_deriv (GnmExpr const *expr,
                GnmEvalPos const *ep,
                GnmExprDeriv *info)
@@ -357,6 +325,7 @@ gnm_expr_deriv (GnmExpr const *expr,
        case GNM_EXPR_OP_LTE:
        case GNM_EXPR_OP_NOT_EQUAL:
        case GNM_EXPR_OP_CAT:
+       case GNM_EXPR_OP_PERCENTAGE:
                // Bail
                return NULL;
 
@@ -364,12 +333,9 @@ gnm_expr_deriv (GnmExpr const *expr,
        case GNM_EXPR_OP_UNARY_PLUS:
                return gnm_expr_deriv (expr->unary.value, ep, info);
 
-       case GNM_EXPR_OP_UNARY_NEG:
-       case GNM_EXPR_OP_PERCENTAGE: {
+       case GNM_EXPR_OP_UNARY_NEG: {
                GnmExpr const *d = gnm_expr_deriv (expr->unary.value, ep, info);
-               return d
-                       ? gnm_expr_new_unary (op, d)
-                       : NULL;
+               return d ? mneg (d, 0) : NULL;
        }
 
        case GNM_EXPR_OP_ADD: {
@@ -404,35 +370,50 @@ gnm_expr_deriv (GnmExpr const *expr,
 
        case GNM_EXPR_OP_EXP: {
                COMMON_BINARY_START
+               GnmFunc *fln = gnm_func_lookup ("ln", NULL);
                GnmValue const *vb = gnm_expr_get_constant (b);
                if (vb && VALUE_IS_FLOAT (vb)) {
                        GnmExpr const *bm1 = gnm_expr_new_constant (value_new_float (value_get_as_float (vb) 
- 1));
                        GnmExpr const *t1 = mexp (a, 1, bm1, 0);
                        gnm_expr_free (db);
                        return mmul (mmul (b, 1, t1, 0), 0, da, 0);
-               } else {
+               } else if (fln) {
                        // a^b = exp(b*log(a))
                        // (a^b)' = a^b * (a'*b/a + b'*ln(a))
                        GnmExpr const *t1 = mdiv (mmul (da, 0, b, 1), 0, a, 1);
-                       GnmFunc *fln = gnm_func_lookup_or_add_placeholder ("LN");
                        GnmExpr const *t2 = mmul
                                (db, 0,
                                 gnm_expr_new_funcall1 (fln, gnm_expr_copy (a)), 0);
                        GnmExpr const *s = madd (t1, 0, t2, 0);
                        return mmul (expr, 1, s, 0);
-               }
+               } else
+                       return NULL;
                COMMON_BINARY_END
        }
 
        case GNM_EXPR_OP_FUNCALL: {
                GnmFunc *f = gnm_expr_get_func_def (expr);
-               const char *fname = gnm_func_get_name (f, FALSE);
-               // Hacks ohoy!
-               if (g_str_equal (fname, "sumsq"))
-                       return gnm_expr_deriv_sumsq (expr, ep, info);
-               if (g_str_equal (fname, "sum"))
-                       return gnm_expr_deriv_sum (expr, ep, info);
-               return NULL;
+               struct DerivInfo const *di = deriv_handlers
+                       ? g_hash_table_lookup (deriv_handlers, f)
+                       : NULL;
+               GnmExpr const *res = di
+                       ? di->handler (expr, ep, info)
+                       : NULL;
+               if (!res)
+                       return NULL;
+
+               if (di->flags & GNM_EXPR_DERIV_CHAIN) {
+                       GnmExpr const *e2 = expr->func.argc == 1
+                               ? gnm_expr_deriv (expr->func.argv[0], ep, info)
+                               : NULL;
+                       if (!e2) {
+                               gnm_expr_free (res);
+                               return NULL;
+                       }
+                       res = mmul (res, 0, e2, 0);
+               }
+
+               return res;
        }
 
        case GNM_EXPR_OP_CONSTANT:
@@ -508,6 +489,10 @@ gnm_expr_top_deriv (GnmExprTop const *texpr,
 {
        GnmExpr const *expr;
 
+       g_return_val_if_fail (GNM_IS_EXPR_TOP (texpr), NULL);
+       g_return_val_if_fail (ep != NULL, NULL);
+       g_return_val_if_fail (info != NULL, NULL);
+
        expr = gnm_expr_deriv (texpr->expr, ep, info);
        if (gnm_debug_flag ("deriv")) {
                GnmParsePos pp;
@@ -527,7 +512,7 @@ gnm_expr_top_deriv (GnmExprTop const *texpr,
                        g_free (s);
                }
        }
-       
+
        return gnm_expr_top_new (expr);
 }
 
@@ -558,11 +543,15 @@ gnm_expr_cell_deriv (GnmCell *y, GnmCell *x)
 gnm_float
 gnm_expr_cell_deriv_value (GnmCell *y, GnmCell *x)
 {
-       GnmExprTop const *dydx = gnm_expr_cell_deriv (y, x);
+       GnmExprTop const *dydx;
        GnmValue *v;
        gnm_float res;
        GnmEvalPos ep;
 
+       g_return_val_if_fail (y != NULL, gnm_nan);
+       g_return_val_if_fail (x != NULL, gnm_nan);
+
+       dydx = gnm_expr_cell_deriv (y, x);
        if (!dydx)
                return gnm_nan;
 
@@ -573,8 +562,45 @@ gnm_expr_cell_deriv_value (GnmCell *y, GnmCell *x)
        value_release (v);
        gnm_expr_top_unref (dydx);
 
-       return res;     
+       return res;
 }
 
+/* ------------------------------------------------------------------------- */
+
+void
+gnm_expr_deriv_install_handler (GnmFunc *func, GnmExprDerivHandler h,
+                               GnmExprDerivFlags flags)
+{
+       struct DerivInfo *data;
+
+       if (!deriv_handlers) {
+               deriv_handlers = g_hash_table_new_full
+                       (g_direct_hash, g_direct_equal,
+                        NULL, g_free);
+       }
+
+       data = g_new (struct DerivInfo, 1);
+       data->handler = h;
+       data->flags = flags;
+
+       g_hash_table_replace (deriv_handlers, func, data);
+}
+
+void
+gnm_expr_deriv_uninstall_handler (GnmFunc *func)
+{
+       g_hash_table_remove (deriv_handlers, func);
+}
+
+/* ------------------------------------------------------------------------- */
+
+void
+_gnm_expr_deriv_shutdown (void)
+{
+       if (deriv_handlers) {
+               g_hash_table_destroy (deriv_handlers);
+               deriv_handlers = NULL;
+       }
+}
 
 /* ------------------------------------------------------------------------- */
diff --git a/src/expr-deriv.h b/src/expr-deriv.h
index f939c0b..415f608 100644
--- a/src/expr-deriv.h
+++ b/src/expr-deriv.h
@@ -17,6 +17,11 @@ void gnm_expr_deriv_info_set_var (GnmExprDeriv *deriv, GnmEvalPos const *var);
 
 /* ------------------------------------------------------------------------- */
 
+GnmExpr const *gnm_expr_deriv (GnmExpr const *expr,
+                              GnmEvalPos const *ep,
+                              GnmExprDeriv *info);
+
+
 GnmExprTop const *gnm_expr_top_deriv (GnmExprTop const *texpr,
                                      GnmEvalPos const *ep,
                                      GnmExprDeriv *info);
@@ -27,6 +32,25 @@ gnm_float gnm_expr_cell_deriv_value (GnmCell *y, GnmCell *x);
 
 /* ------------------------------------------------------------------------- */
 
+GnmExprList *gnm_expr_deriv_collect (GnmExpr const *expr,
+                                    GnmEvalPos const *ep,
+                                    GnmExprDeriv *info);
+
+typedef GnmExpr const * (*GnmExprDerivHandler) (GnmExpr const *expr,
+                                               GnmEvalPos const *ep,
+                                               GnmExprDeriv *info);
+typedef enum {
+       GNM_EXPR_DERIV_NO_CHAIN = 0x0,
+       GNM_EXPR_DERIV_CHAIN = 0x1
+} GnmExprDerivFlags;
+
+void gnm_expr_deriv_install_handler (GnmFunc *func, GnmExprDerivHandler h,
+                                    GnmExprDerivFlags flags);
+void gnm_expr_deriv_uninstall_handler (GnmFunc *func);
+void _gnm_expr_deriv_shutdown (void);
+
+/* ------------------------------------------------------------------------- */
+
 G_END_DECLS
 
 #endif
diff --git a/src/func-builtin.c b/src/func-builtin.c
index f6e65a2..9548b43 100644
--- a/src/func-builtin.c
+++ b/src/func-builtin.c
@@ -30,6 +30,7 @@
 #include <value.h>
 #include <selection.h>
 #include <expr.h>
+#include <expr-deriv.h>
 #include <expr-impl.h>
 #include <sheet.h>
 #include <cell.h>
@@ -63,6 +64,36 @@ gnumeric_sum (GnmFuncEvalInfo *ei, int argc, GnmExprConstPtr const *argv)
                                     GNM_ERROR_VALUE);
 }
 
+static GnmExpr const *
+gnumeric_sum_deriv (GnmExpr const *expr,
+                   GnmEvalPos const *ep,
+                   GnmExprDeriv *info)
+{
+       GnmExprList *l, *args = gnm_expr_deriv_collect (expr, ep, info);
+       GnmFunc *fsum = gnm_expr_get_func_def (expr);
+       gboolean bad = FALSE;
+
+       for (l = args; l; l = l->next) {
+               GnmExpr const *e = l->data;
+               GnmExpr const *d = gnm_expr_deriv (e, ep, info);
+               if (d) {
+                       gnm_expr_free (e);
+                       l->data = (gpointer)d;
+               } else {
+                       bad = TRUE;
+                       break;
+               }
+       }
+
+       if (bad) {
+               for (l = args; l; l = l->next)
+                       gnm_expr_free (l->data);
+               gnm_expr_list_free (args);
+               return NULL;
+       } else
+               return gnm_expr_new_funcall (fsum, args);
+}
+
 /***************************************************************************/
 
 static GnmFuncHelp const help_product[] = {
@@ -478,6 +509,10 @@ func_builtin_init (void)
        gname = N_("Logic");
        logic_group = gnm_func_group_fetch (gname, _(gname));
        gnm_func_add (logic_group, builtins + i++, textdomain);
+
+       gnm_expr_deriv_install_handler (gnm_func_lookup ("sum", NULL),
+                                       gnumeric_sum_deriv,
+                                       GNM_EXPR_DERIV_NO_CHAIN);
 }
 
 static void
diff --git a/src/libgnumeric.c b/src/libgnumeric.c
index 45f125e..bf816ed 100644
--- a/src/libgnumeric.c
+++ b/src/libgnumeric.c
@@ -50,6 +50,7 @@
 #include "clipboard.h"
 #include "value.h"
 #include "expr.h"
+#include "expr-deriv.h"
 #include "parse-util.h"
 #include "rendered-value.h"
 #include "gnumeric-conf.h"
@@ -385,6 +386,7 @@ gnm_shutdown (void)
        dependent_types_shutdown ();
        clipboard_shutdown ();
        gnm_sheet_cell_shutdown ();
+       _gnm_expr_deriv_shutdown ();
        _gnm_expr_shutdown ();
        parse_util_shutdown ();
        value_shutdown ();


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]