Commit 84d7e60c authored by Morten Welinder's avatar Morten Welinder

Derivatives: simplify expressions further.

Also add DERIV function, but only during the test suite.
parent 476351d0
......@@ -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,
......
......@@ -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
*/
......
......@@ -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,
......
......@@ -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);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment