Kernel
A kernel, also called a generalized variable, is the unit that polynomial algorithms in a computer algebra system treat as a variable[1][2]. The algorithms for expanding, factoring, taking gcds or simplifying rational functions work on polynomials in variables, but expressions contain other things: , , , . Each maximal subexpression of this kind is replaced by a fresh variable, the polynomial algorithm runs, and the variables are replaced back. To expand , the system expands and returns . The approach is simple and underlies the rational-function forms of REDUCE and Macsyma[2][3], but it sees no relations among kernels: as polynomials in their kernels, and , or and , are different, and recognizing their equality needs more than polynomial arithmetic.
Identifying kernels
The kernels of an expression are found by descending through sums, products and non-negative integer powers; whatever else is met, a symbol, a function application, a negative or fractional power, is a kernel. Equal kernels must become the same variable, or nothing would combine, so the arguments of each kernel are simplified to canonical form first, recursively, and the kernel is then identified by its canonical printed form. With that, and are one kernel[1].
Expressions as polynomials in kernels: the conversion, which simplifies the arguments of each kernel before naming it, and the conversion back.
(* A polynomial in kernels: terms map an exponent list over the kerneltable to a rational coefficient. *)module M = Map.Make (struct type t = int list let compare = compare end)type poly = Q.t M.t(* Kernels are identified by their canonical printed form, so equalsubexpressions become the same variable. *)let kernels : (string * e) list ref = ref []let kernel_index (k : e) (key : string) =let rec find i = function| [] -> kernels := !kernels @ [ (key, k) ]; i| (s, _) :: rest -> if s = key then i else find (i + 1) rest infind 0 !kernelslet mono_of i = List.init (i + 1) (fun j -> if j = i then 1 else 0)let rec pad a b = match a, b with| [], [] -> [] | x :: a, [] -> x :: pad a [] | [], y :: b -> y :: pad [] b | x :: a, y :: b -> (x + y) :: pad a blet trim m = let r = List.rev m in let rec drop = function 0 :: t -> drop t | l -> l in List.rev (drop r)let padd p q = M.union (fun _ a b -> let s = Q.add a b in if Q.equal s Q.zero then None else Some s) p qlet pmul p q = M.fold (fun m1 c1 acc -> M.fold (fun m2 c2 acc ->padd acc (M.singleton (trim (pad m1 m2)) (Q.mul c1 c2))) q acc) p M.emptylet pconst c = if Q.equal c Q.zero then M.empty else M.singleton [] clet rec ppow p n = if n = 0 then pconst Q.one else pmul p (ppow p (n - 1))(* The canonical polynomial form, with kernels simplified recursivelybefore they are named. *)let rec to_poly (x : e) : poly =match x with| Num q -> pconst q| Add l -> List.fold_left (fun acc t -> padd acc (to_poly t)) M.empty l| Mul l -> List.fold_left (fun acc t -> pmul acc (to_poly t)) (pconst Q.one) l| Pow (b, n) when n >= 0 -> ppow (to_poly b) n| Sym _ | Pow _ | App _ ->let k = match x with App (f, a) -> App (f, of_poly (to_poly a)) | Pow (b, n) -> Pow (of_poly (to_poly b), n) | k -> k inlet key = show_kernel k inM.singleton (mono_of (kernel_index k key)) Q.oneand show_kernel k = show k(* Back to an expression, terms in decreasing total degree. *)and of_poly (p : poly) : e =let term (m, c) =let factors = List.concat (List.mapi (fun i d ->if d = 0 then [] else let k = snd (List.nth !kernels i) in [ if d = 1 then k else Pow (k, d) ]) m) inmatch factors with| [] -> Num c| fs -> if Q.equal c Q.one then (match fs with [ f ] -> f | fs -> Mul fs) else Mul (Num c :: fs) inlet terms = List.sort (fun (a, _) (b, _) -> compare (List.fold_left ( + ) 0 b, b) (List.fold_left ( + ) 0 a, a)) (M.bindings p) inmatch List.map term terms with [] -> Num Q.zero | [ t ] -> t | ts -> Add tslet simplify x = of_poly (to_poly x)let is_zero x = M.is_empty (to_poly x)
Expanding with kernels, and recognizing kernels that differ only in how their arguments are written.
open Expr
Running it.
(sin(x) + 1)^2 -> sin(x)^2 + 2·sin(x) + 1 kernels: sin(x)(sqrt(x) + 1)^2 -> sqrt(x)^2 + 2·sqrt(x) + 1 kernels: sqrt(x)(x^-1 + 1)^2 -> x^(-2) + 2·x^(-1) + 1 kernels: x^(-1)sin(x + x) - sin(2x) -> 0 kernels: noneexp((x+1)^2) - exp(x^2+2x+1) -> 0 kernels: none
Relations between kernels
Treating kernels as independent variables is correct only if they are algebraically independent. Often they are not. , , for positive , and . A polynomial form in dependent kernels is still a correct representation, but not a canonical one: expressions equal to zero may not be recognized[9].
Rewriting exponentials of integer multiples and squares of square roots before naming kernels.
open Expr(* exp(2x) and exp(x) are different kernels but not independent. Rewritingexp(n u) as exp(u)^n for integer n, and sqrt(u)^2 as u, before namingkernels removes these relations. *)let rec normalize_kernels (x : e) : e =match x with| App ("exp", a) ->(match simplify (normalize_kernels a) with| Mul (Num n :: rest) when Z.equal (Q.den n) Z.one && Q.sign n > 0 ->let u = match rest with [ u ] -> u | us -> Mul us inPow (App ("exp", u), Z.to_int (Q.num n))| a -> App ("exp", a))| Pow (App ("sqrt", a), n) when n >= 2 ->Mul [ Pow (normalize_kernels a, n / 2); Pow (App ("sqrt", normalize_kernels a), n mod 2) ]| App (f, a) -> App (f, normalize_kernels a)| Add l -> Add (List.map normalize_kernels l)| Mul l -> Mul (List.map normalize_kernels l)| Pow (b, n) -> Pow (normalize_kernels b, n)| Num _ | Sym _ -> x
Running it.
exp(2x) - exp(x)^2 zero as written: false after normalizing kernels: trueexp(3x) - exp(x)exp(2x) zero as written: false after normalizing kernels: truesqrt(x)^2 - x zero as written: false after normalizing kernels: truesin(x)^2 + cos(x)^2 - 1 zero as written: false after normalizing kernels: false
The rewriting reduces to a power of and algebraic kernels modulo their defining equations, which removes these relations. The trigonometric identity survives, because and are related by a polynomial equation that is not a rewriting of one kernel into another; it is handled either by reducing modulo with a Gröbner basis, or by rewriting both in terms of .
Structure theorems
For elementary functions, built from and constants by arithmetic, exponentials, logarithms and algebraic operations, the question of which kernels are independent has a precise answer. Risch's structure theorem of 1979 states that a set of exponentials and logarithms is algebraically dependent over the rational functions only through linear relations among their arguments and the logarithms: are dependent exactly when some integer combination of the is constant, and similarly for logarithms[4]. Rosenlicht gave a related account of Liouville's theory[5]. Algorithms can therefore choose an algebraically independent set of kernels and express the others in terms of them, which gives canonical forms for large classes of exponential and logarithmic expressions. Brown had studied the rational exponential expressions in this way in 1969[6]. These results are also the foundation of the Risch integration algorithm, which works in a tower of such independent extensions.
In computer algebra systems
REDUCE calls such objects kernels and represents expressions as rational functions in them, ordering kernels to make the representation canonical up to kernel dependence[2]. Macsyma's canonical rational expressions, created by its rat command, do the same, and Moses described the design choices in 1971[3]. Textbooks call them generalized variables[1][7], and every general system has an equivalent: an expression is a polynomial or rational function in whatever it cannot look inside[8].
History
REDUCE, Anthony Hearn's system begun in the 1960s, uses the term kernel for the operator expressions that its simplifier treats as the variables of polynomials[2]. The Macsyma project at MIT developed canonical rational expressions in the same period[3]. The theoretical questions of independence were taken up by Caviness[9], Brown[6] and Risch[4], and their structure theorems made it possible to choose independent kernels systematically for elementary functions.
see also
- Canonical formIn computer algebra, a canonical form is a representation in which mathematically equal expressions are written identically, so that equality becomes a syntactic comparison and zero-testing a look at the representation. A normal form is the weaker requirement that expressions equal to zero are represented by 0. Expanded polynomials with sorted terms, reduced rational functions and remainders modulo a Gröbner basis are canonical; for expressions with exponentials, logarithms and absolute values, Richardson proved in 1968 that zero-equivalence is undecidable, so no canonical form can exist. Where no canonical form is practical, randomized evaluation, justified by the Schwartz–Zippel lemma, tests identities with high confidence.
- Risch algorithmThe Risch algorithm is a decision procedure for integration in finite terms: given an elementary function, it either finds an elementary antiderivative or proves that none exists. It rests on Liouville's theorem, which says that an elementary antiderivative, if one exists, is a function of the same field plus constant multiples of logarithms. The algorithm writes the integrand in a tower of logarithmic, exponential and algebraic extensions of the rational functions and works down it, using Hermite reduction, the Rothstein–Trager resultant and the Risch differential equation y′ + fy = g. Its transcendental case is completely algorithmic; its algebraic case is much harder; and both are decision procedures only relative to deciding whether a constant is zero.
- Gröbner basisA Gröbner basis of an ideal of multivariate polynomials is a generating set with respect to which multivariate division behaves as in one variable: the remainder of any polynomial is unique, and it is zero exactly when the polynomial lies in the ideal. Formally, the leading terms of the basis, for a fixed monomial order, generate the ideal of all leading terms. Buchberger's algorithm computes one from any generating set by adding the remainders of S-polynomials until they all reduce to zero. Gröbner bases decide ideal membership, solve systems of polynomial equations, count their solutions and eliminate variables; Bruno Buchberger introduced them in 1965.
- Monomial orderA monomial order is a total order on the monomials of a polynomial ring that is compatible with multiplication and has 1 as its smallest element. It determines the leading term of every polynomial, and with it the result of multivariate division and the shape of Gröbner bases. The standard choices are lexicographic order, which compares exponents variable by variable and is used for eliminating variables; graded lexicographic order; and graded reverse lexicographic order, which usually gives the smallest Gröbner bases and the fastest computations. Dickson's lemma guarantees that every monomial order is a well-order, so that division and Buchberger's algorithm terminate.
further reading
- [1]J. S. Cohen, Computer Algebra and Symbolic Computation: Mathematical Methods, A K Peters (2003).
- [2]A. C. Hearn, REDUCE User’s Manual, version 3.8, Konrad-Zuse-Zentrum Berlin (2004).
- [3]J. Moses, “Algebraic simplification: a guide for the perplexed”, Communications of the ACM 14 (1971).
- [4]R. H. Risch, “Algebraic properties of the elementary functions of analysis”, American Journal of Mathematics 101 (1979).
- [5]M. Rosenlicht, “On Liouville’s theory of elementary functions”, Pacific Journal of Mathematics 65 (1976).
- [6]W. S. Brown, “Rational exponential expressions and a conjecture concerning π and e”, American Mathematical Monthly 76 (1969).
- [7]K. O. Geddes, S. R. Czapor, G. Labahn, Algorithms for Computer Algebra, ch. 3, Kluwer (1992).
- [8]J. H. Davenport, Y. Siret, E. Tournier, Computer Algebra: Systems and Algorithms for Algebraic Computation, Academic Press (1988).
- [9]B. F. Caviness, “On canonical forms and simplification”, Journal of the ACM 17 (1970).
last updated