:- module(linearize).
:- export simplify / 2.
:- export normalize / 3.
:- export quadnorm / 6.
:- export polydenorm / 2.
:- export polyrenorm / 2.
:- export polynorm / 3.
:- export linearize_body / 4.
:- export delinearize / 2.
:- export linrenorm / 2.
:- export linearize / 3.
:- tool(linearize / 3,linearize_body / 4).
:- comment(linearize_body / 4, hidden).
:- comment(summary, "Normalizers for arithmetic expressions").
:- comment(author, "Joachim Schimpf, IC-Parc").
:- comment(date, "$Date: 2006/09/23 01:53:50 $").
:- comment(copyright, "Cisco Systems, Inc.").
:- comment(delinearize / 2, [summary : "Convert a linear form back to a standard arithmetic expression", amode : delinearize(?, -), args : ["Linear" : "Normalized lists of monomials", "Expression" : "Arithmetic expression with constants and variables"], see_also : [linrenorm / 2, linearize / 3], eg : "\n    ?- linearize(3*X-7*Y+2*(X+Y), L, R), delinearize(L, E).\n    X = X\n    Y = Y\n    R = []\n    L = [0 * 1, 5 * X, -5 * Y]\n    E = 5 * X - 5 * Y\n    yes.\n    "]).
:- comment(linearize / 3, [summary : "Split and arithmetic expression into linear and nonlinear parts", amode : linearize(?, -, -), args : ["Expression" : "Arithmetic expression with constants and variables", "Linear" : "Normalized lists of monomials", "Residue" : "Residual nonlinear components in the form AuxVar=Expr"], see_also : [polynorm / 3, linrenorm / 2, delinearize / 2], desc : html("\tThe linear component of expression is normalised into a list\n\t(sum) of monomials of the form\n<PRE>\n\t\t[C0*1, C1*X1, C2*X2, ...]\n</PRE>\n\twhere Ci are numbers and Xi are distinct variables.\n\tThe first (constant) term is always present, Ci (i>=1) are nonzero.\n<P>\n\tlinearize/3 converts a general arithmetic expression into a\n\tnormalised linear form. The nonlinear parts are extracted using\n\tauxiliary variables, and returned as Residue, which is a list of\n\tAux=NonLinExpr constraints.\n<P>\n\tlinearize/3 understands all arithmetic expressions plus\n<PRE>\n\t    lin(Lin)\n</PRE>\n\twhere Lin is an already normalised linear expression. All variables\n\twithin Expression (which are free at linearization time) are taken\n\tto be numerical variables. If you intend to have variables which can\n\tbe bound to symbolic expressions rather than numbers, they must be\n\twrapped into an eval/1 functor.\n    "), eg : "\n    ?- linearize(3*X-7*Y+2*(X+Y), L, R).\n    X = X\n    Y = Y\n    L = [0 * 1, 5 * X, -5 * Y]\n    R = []\n    yes.\n\n    ?- linearize(12*3, L, R).\n    L = [36 * 1]\n    R = []\n    yes.\n\n    ?- linearize(X*(3+7*Y)-X, L, R).\n    Y = Y\n    X = X\n    L = [0 * 1, 1 * _308, 2 * X]\n    R = [_308 = X * (7 * Y)]\n    yes.\n    "]).
:- comment(linrenorm / 2, [summary : "Renormalize a linear form", amode : linrenorm(+, -), args : ["LinOld" : "Possibly denormal lists of monomials", "LinNew" : "Normalized lists of monomials"], see_also : [linearize / 3, delinearize / 2], desc : html("\tThe normal form of linear expressions is\n<PRE>\n\t\t[C0*1, C1*X1, C2*X2, ...]\n</PRE>\n\twhere Ci are numbers and Xi are distinct variables.\n\tThe first (constant) term is always present, Ci (i>=1) are nonzero.\n<P>\n\tSuch a form can become denormalized due to unifications\n\t(instantiation or variable-variable aliasing). This predicate\n\trenormalizes it. Note that variables may only become instantiated\n\tto numbers!\n    "), eg : "\n    ?- linearize(3*X-7*Y+2*(X+Y), L1, R), writeln(L1),\n\tY = 3,\n\tlinrenorm(L1,L2), writeln(L2).\n\n    [0 * 1, 5 * X, -5 * Y]\n    [-15 * 1, 5 * X]\n    "]).
:- comment(polydenorm / 2, [summary : "Convert a polynomial form back to a standard arithmetic expression", amode : polydenorm(?, -), args : ["NormPoly" : "Normalized polynomial form", "Expression" : "Arithmetic expression with constants and variables"], see_also : [polyrenorm / 2, polynorm / 3], eg : "\n    ?- polynorm(2*5 + 3*(X+5*Y+7)*Z, Poly, []), polydenorm(Poly, Expr).\n    X = X\n    Y = Y\n    Z = Z\n    Poly = [[[10]], [[21, Z]], [[3, X, Z], [15, Y, Z]]]\n    Expr = 10 + 21 * Z + 3 * X * Z + 15 * Y * Z\n    yes.\n    "]).
:- comment(polynorm / 3, [summary : "Extracts and normalises the polynomial part of an arithmetic expression", amode : polynorm(?, -, -), args : ["Expression" : "Arithmetic expression with constants and variables", "NormPoly" : "Normalized polynomial", "Residue" : "Residual nonpolynomial components in the form AuxVar=Expr"], see_also : [polydenorm / 2, polyrenorm / 2, linearize / 3], desc : html("\tThis predicate converts a general arithmetic expression into a\n\tnormalized polynomial representation and possibly a nonpolynomial\n\tresidue. The normalized polynomial representation is a follows:\n<P>\n\tA <EM>monomial</EM> is a list of constants and variables and represents\n\tthe product of all the list elements.\n\tIn a <EM>normalised monomial</EM>, the list is sorted, the first element is\n\tthe (only) constant and the others are variables.\n<P>\n\tA <EM>normalised polynomial</EM> is represented as a list of lists of\n\tnormalised monomials. The sublists represent groups of monomials\n\tof the same degree in ascending order. If there are no monomials\n\tfor a certain degree, the list element is missing:\n<PRE>\n\t[ConstantMonos, LinearMonos, QuadraticMonos, CubicMonos, ...]\n</PRE>\n\tIn a normalised polynomial, all monomials are normalised and\n\tall monomials with identical variables are merged.\n<P>\n\tNon-polynomial components are factored out by introducing an\n\tauxiliary variable and adding a term Aux=NonPolyExpr to the\n\tResidue result list.  All variables within Expression (which\n\tare free at normalization time) are taken to be numerical\n\tvariables.  If you intend to have variables which can be bound\n\tto symbolic expressions rather than number, they must be\n\twrapped into an eval/1 functor.\n    "), eg : "\n    ?- polynorm(2*5 + 3*(X+5*Y+7)*Z, Poly, Res).\n    X = X\n    Y = Y\n    Z = Z\n    Poly = [[[10]], [[21, Z]], [[3, X, Z], [15, Y, Z]]]\n    Res = []\n    yes.\n\n    ?- polynorm(3*(X+Y),  Poly, Res).\n    X = X\n    Y = Y\n    Poly = [[[3, X], [3, Y]]]\n    Res = []\n    yes.\n\n    ?- polynorm(3, Poly, Res).\n    Poly = [[[3]]]\n    Res = []\n    yes.\n    "]).
:- comment(polyrenorm / 2, [summary : "Renormalize a polynomial form", amode : polyrenorm(+, -), args : ["PolyOld" : "Possibly denormal polynomial form", "PolyNew" : "Normalized polynomial form"], see_also : [polynorm / 3, polydenorm / 2], desc : html("\tSee polynorm/3 for the definition of the polynomial form.\n\tSuch a form can become denormalized due to unifications\n\t(instantiation or variable-variable aliasing). This predicate\n\trenormalizes it.\n    "), eg : "\n    ?- polynorm(3*(X+Y),  Poly1, []), writeln(Poly1),\n\tY = 3,\n\tpolyrenorm(Poly1, Poly2), writeln(Poly2).\n\n    [[[3, X], [3, Y]]]\n    [[[9]], [[3, X]]]\n    "]).
:- comment(quadnorm / 6, [summary : "Extracts constant, linear and quadratic part of an arithmetic expression", amode : quadnorm(?, -, -, -, -, -), args : ["Expression" : "Arithmetic expression with constants and variables", "Const" : "Variable or number", "Linear" : "Variable or normalized linear polynomial", "Quadratic" : "Variable or normalized quadratic polynomial", "PolyRes" : "Variable or normalized superquadratic polynomial", "Residue" : "Residual nonpolynomial components in the form AuxVar=Expr"], see_also : [polynorm / 3, linearize / 3], desc : html("\tThis predicate is a simplified interface to polynorm/3 for the case\n\twhere one is only interested in linear and quadratic components.\n\tSee polynorm/3 for details.\n    "), eg : "\n    ?- quadnorm(2*5 + 3*(X+5*Y+7)*Z, Const, Lin, Quad, Poly, Res).\n    X = X\n    Y = Y\n    Z = Z\n    Const = 10\n    Lin = [[21, Z]]\n    Quad = [[3, X, Z], [15, Y, Z]]\n    Poly = []\n    Res = []\n    yes.\n    "]).
