#+Title: LARCS #+Subtitle: The "Lisp Automated Rewrite and Calculation System" #+AUTHOR: Samuel W. Flint #+EMAIL: swflint@flintfam.org #+DATE: \today #+INFOJS_OPT: view:info toc:nil path:http://flintfam.org/org-info.js #+OPTIONS: toc:nil H:5 ':t *:t todo:nil stat:nil d:nil #+PROPERTY: header-args :noweb no-export :comments noweb #+LATEX_HEADER: \usepackage[margins=0.75in]{geometry} #+LATEX_HEADER: \lstset{texcl=true,breaklines=true,columns=fullflexible,basicstyle=\ttfamily,frame=lines,literate={<=}{$\leq$}1 {>=}{$\geq$}1} #+LATEX_CLASS: report #+LATEX_CLASS_OPTIONS: [10pt,twoside] * Export :noexport: :PROPERTIES: :CREATED: <2016-06-09 Thu 12:49> :END: #+Caption: Export Document #+Name: export-document #+BEGIN_SRC emacs-lisp :exports none :results none (save-buffer) (let ((org-confirm-babel-evaluate (lambda (lang body) (declare (ignorable lang body)) nil))) (org-latex-export-to-pdf)) #+END_SRC * Tangle :noexport: :PROPERTIES: :CREATED: <2016-06-09 Thu 12:50> :END: #+Caption: Tangle Document #+Name: tangle-document #+BEGIN_SRC emacs-lisp :exports none :results none (save-buffer) (let ((python-indent-offset 4)) (org-babel-tangle)) #+END_SRC * DONE Introduction CLOSED: [2016-12-14 Wed 14:59] :PROPERTIES: :CREATED: <2016-06-09 Thu 09:19> :UNNUMBERED: t :ID: f3e3cdb9-a661-4598-8be1-e15f587f35bb :END: LARCS is a Computer Algebra System, written from scratch, using the technique of literate programming to accomplish the goal. The purpose of this is threefold: first, as a learning exercise; second, to develop a CAS that is built around a clean, readable and modular design, rather than efficiency; and third, to demonstrate the abilities of Literate Programming in general, and Org-Mode in particular. There is another motive, many CASs are non-free, and those that aren't don't always provide a useful programming interface, and with the goal of a clean, readable and modular design, this can be accomplished. * DONE What's in a name? CLOSED: [2016-12-14 Wed 15:13] :PROPERTIES: :CREATED: <2016-06-09 Thu 12:37> :UNNUMBERED: t :END: While the [[id:f3e3cdb9-a661-4598-8be1-e15f587f35bb][Introduction]] describes the purpose of LARCS, it does not describe the name. LARCS stands for Lisp Automated Rewrite and Calculation System. This name describes the system quite accurately, as LARCS was written in Lisp, provides an automated system for rewriting equations into various forms, and calculating given an equation and values of the various variables, and acts as a coherent grouping of libraries forming a system. * TOC :ignore: :PROPERTIES: :CREATED: <2016-06-09 Thu 09:19> :END: #+TOC: headlines 3 #+TOC: listings * WORKING Expression Types [1/4] :PROPERTIES: :ID: fe611e8f-db42-4fe0-8e77-9cfb55d2227c :END: All expressions are built from various ~~ objects. These objects are specialized to contain only certain, specific information, relevant to a particular expression. The most basic expressions (besides expression itself) are ~~ (holding things like numbers or variables) and ~~ (holding things like multiplicatinos, divisions, exponents, trigonometric expressions, arithmetic expressions, /etc./). All subtypes of ~~ must know if they are atomic, so we define a generic for this, they must also tell if they are ~eqal~ (a form of equality), and be able to perform substitution, evaluation, and simplification, however, the latter three are implemented elsewhere. The organization of the various types may be found in Figure~[[fig:expression-types]]. Note, by default, ~eqal~ will handle ~type-error~ by trying to switch the order of the arguments. If this fails, it will warn that an applicable method for the given types, and return ~nil~. #+Caption: Basic Expressions Types #+Name: type-basic #+BEGIN_SRC lisp @export (defclass () ()) @export (defgeneric atomicp (expression)) @export (defgeneric eqal (expression-a expression-b)) (defmethod eqal ((expression-a ) (expression-b )) (if (> (length (compute-applicable-methods #'eqal (list expression-b expression-a))) 2) (eqal expression-b expression-a) (warn "No definition of `eqal' for types `~a' and `~a'." (type-of expression-a) (type-of expression-b)))) (defmethod eqal :around ((expression-a ) (expression-b )) (handler-case (call-next-method) (type-error (err) (declare (ignore err)) (eqal expression-b expression-a)))) @export (defgeneric substitute-expression (expression with in)) @export (defgeneric evaluate (expression &key substitutions-list resolve-constants-light-p resolve-constants-hard-p)) @export (defgeneric simplify (expression &key resolve-constants-light-p resolve-constants-hard-p)) @export (defgeneric to-sexp (expression &optional resolve-constants-p)) #+END_SRC #+Caption: Types #+Name: types-image #+BEGIN_SRC dot :file "img/types.png" :export results :cache yes digraph { expression [label = ""]; atomic [label = ""]; number [label = ""]; variable [label = ""]; constant [label = ""]; compound [label = ""]; mult [label = ""]; div [label = ""]; expt [label = ""]; exp [label = ""]; trig [label = ""]; log [label = ""]; ln [label = ""]; add [label = ""]; sub [label = ""]; polyTerm [label = ""]; expression -> atomic; atomic -> number -> constant; atomic -> variable -> constant; expression -> compound; compound -> mult -> polyTerm; compound -> div; compound -> expt -> exp; expt -> polyTerm; compound -> trig; compound -> add; compound -> sub; compound -> log -> ln; variable -> polyTerm; } #+END_SRC #+Caption: Expression Type Diagram #+Name: fig:expression-types #+ATTR_LATEX: :width \linewidth #+RESULTS[da7866e208592356402f41a6924a01f05cdaae36]: types-image [[file:img/types.png]] ** DONE Atomic Types [3/3] :PROPERTIES: :ID: 9be57f5d-ed0e-4a7c-9c06-a4c647f6d56e :END: Of the various types, there are three atomic types, ~~, ~~ and ~~ They are very similar, only differing on the ~:type~ their value slot has. As all ~~ subtypes are, in fact, atomic (save for some later blended types), we can simply return ~t~ for ~atomicp~. Further, all ~~ have a definite value, so we should be able to retrieve this. #+Caption: Atomic Type Definition #+Name: type-atomic #+BEGIN_SRC lisp @export (defclass () ()) @export (defgeneric value (expression &key resolve-constants)) (defmethod atomicp ((expression )) (declare (ignore expression)) t) #+END_SRC *** DONE Numbers CLOSED: [2019-01-16 Wed 16:01] :PROPERTIES: :ID: f0314608-46d9-4cc0-a3e1-897481827ddf :END: As an ~~, ~~ is rather simple, having only one slot, ~value~, readable using the ~value~ method, and initialized with ~:value~. This ~value~ must be of type ~number~. Two ~~ objects are equal, if their ~value~ is ~=~. #+Caption: Numbers #+Name: type-numbers #+BEGIN_SRC lisp @export (defclass () ((value :initarg :value :type number))) (defmethod value ((expression ) &key resolve-constants) (declare (ignore resolve-constants)) (slot-value expression 'value)) (defmethod eqal ((expression-a ) (expression-b )) (= (value expression-a) (value expression-b))) (defmethod to-sexp ((expression ) &optional resolve-constants-p) (declare (ignorable resolve-constants-p)) (slot-value expression 'value)) #+END_SRC *** DONE Variables CLOSED: [2019-01-16 Wed 16:13] :PROPERTIES: :ID: f5f656d0-6253-4c4c-8c50-6622d685eaa1 :END: ~~ is similar to ~~, with the only difference being ~name~ must be a ~symbol~ or ~keyword~. Two ~~ objects are ~eqal~ if the ~name~ of each is ~equalp~. #+Caption: Variables #+Name: type-variables #+BEGIN_SRC lisp @export (defclass () ((name :initarg :name :type (or symbol keyword)))) (defmethod value ((expression ) &key resolve-constants) (declare (ignore resolve-constants)) (slot-value expression 'name)) (defmethod eqal ((expression-a ) (expression-b )) (equalp (value expression-a) (value expression-b))) (defmethod to-sexp ((expression ) &optional resolve-constants-p) (declare (ignore resolve-constants-p)) (slot-value expression 'name)) #+END_SRC *** DONE Constants CLOSED: [2019-01-16 Wed 16:23] :PROPERTIES: :ID: 806bbce0-ab52-411a-8178-29bda6bbb36a :END: ~~ is a bit different from the rest -- it has a name, as well as a number, and the value depends on whether or not you want to "resolve" the constant. If the constant is to be resolved, the numeric value is returned, otherwise, the name is returned. Equality of constants is a bit different, and is checked as follows: - ~~, ~~ :: Check if ~name~ is ~equalp~. - ~~, ~~ :: Check if ~name~ is ~equalp~. - ~~, ~~ :: Check if ~value~ is ~=~. #+Caption: Constants #+Name: type-constants #+BEGIN_SRC lisp @export (defclass ( ) ((name :initarg :name :type (or symbol keyword)) (value :initarg :value :type number))) (defmethod value ((expression ) &key resolve-constants) (if resolve-constants (slot-value expression 'value) (slot-value expression 'name))) (defmethod eqal ((expression-a ) (expression-b )) (equalp (slot-value expression-a 'name) (slot-value expression-b 'name))) (defmethod eqal ((expression-a ) (expression-b )) (equalp (slot-value expression-a 'name) (slot-value expression-b 'name))) (defmethod eqal ((expression-a ) (expression-b )) (= (slot-value expression-a 'value) (slot-value expression-b 'value))) (defmethod to-sexp ((expression ) &optional resolve-constants-p) (if resolve-constants-p (slot-value expression 'value) (slot-value expression 'name))) #+END_SRC ** WORKING Compound Types [0/7] :PROPERTIES: :ID: a0d2eb19-8a1e-4dee-9b41-c454a49cacc4 :END: Compound types are the majority of expressions. An atom is well and good, but often says relatively little. We use compound types to form equations from other expressions. These are not, then, ~atomicp~. #+Caption: Compound Types #+Name: type-compound #+BEGIN_SRC lisp @export (defclass () ()) (defmethod atomicp ((expression )) (declare (ignore expression)) nil) #+END_SRC *** WORKING Additions :PROPERTIES: :ID: a1a01fb4-59e1-4459-9ab9-d32269af77dd :END: ~~ is simple, an ~~ has only a list of terms. As with ~~, two ~~ objects are equal if they have the same number of terms and all terms are the same, order not withstanding (as addition is commutative). #+Caption: Additions #+Name: type-additions #+BEGIN_SRC lisp @export (defclass () ((terms :reader terms :initarg :terms :type (list )))) (defmethod eqal ((expression-a ) (expression-b )) (let ((terms-a (terms expression-a)) (terms-b (terms expression-b))) (and (= (length terms-a) (length terms-b)) (null (set-difference terms-a terms-b :test #'eqal))))) #+END_SRC *** WORKING Subtractions :PROPERTIES: :ID: aad16971-81c8-48da-aaa5-bb84b03d6912 :END: Subtractions again, contain only a list of terms. However, unlike other types having only a list of terms, the terms are not a set, and therefore order as well as content must be verified to check ~eqal~-ness. #+Caption: Subtractions #+Name: type-subtractions #+BEGIN_SRC lisp @export (defclass () ((terms :reader terms :initarg :terms :type (list )))) (defmethod eqal ((expression-a ) (expression-b )) (let ((a-terms (terms expression-a)) (b-terms (terms expression-b))) (and (= (length a-terms) (length b-terms)) (do* ((keep (eqal (first a-terms) (first b-terms)) (eqal (first a) (first b))) (a a-terms (rest a)) (b b-terms (rest b))) ((or (not keep) (null a) (null b)) keep))))) #+END_SRC *** WORKING Multiplications :PROPERTIES: :ID: 0c601f5c-a614-4c30-9107-220a5d45a334 :END: Multiplication is one of the more frequently used expression types, and is, surprisingly, simple. A ~~ has only one slot, ~terms~, the various expressions to be multiplied. Further, if two ~~ objects have the same number of terms, and there is no ~set-difference~ given ~eqal~ between the two lists of terms, the two ~~ objects are ~eqal~. #+Caption: Multiplications #+Name: type-multiplications #+BEGIN_SRC lisp @export (defclass () ((terms :reader terms :initarg :terms :type (list )))) (defmethod eqal ((expression-a ) (expression-b )) (and (= (length (terms expression-a)) (length (terms expression-b))) (null (set-difference (terms expression-a) (terms expression-b) :test #'eqal)))) #+END_SRC *** WORKING Divisions :PROPERTIES: :ID: bcc56ff0-0a6f-4eb6-8185-2cab3eed99da :END: Division is similar to ~~, although instead of having ~terms~ it has ~numerator~ and ~denominator~. A ~~ is equal to another if the ~numerator~ of the first is ~eqal~ to the second and likewise for the denominator. #+Caption: Divisions #+Name: type-division #+BEGIN_SRC lisp @export (defclass () ((numerator :reader div-numerator :initarg :numerator :type ) (denominator ::reader div-denominator :initarg :denominator :type ))) (defmethod eqal ((expression-a ) (expression-b )) (and (eqal (div-numerator expression-a) (div-numerator expression-b)) (eqal (div-denominator expression-a) (div-denominator expression-b)))) #+END_SRC *** WORKING Exponentials :PROPERTIES: :ID: d8c87457-5c3b-4263-ba60-53b9b9f5e816 :END: There are two primary forms of exponential -- the natural (~exp~) and the general (~expt~). Given this, there are two classes, with ~~ being a subclass of ~~, mirroring the generality of exponentials in general. Two ~~ objects are ~eqal~ iff their ~base~ and ~exponent~ are ~eqal~. #+Caption: Exponentials #+Name: type-exponentials #+BEGIN_SRC lisp @export (defclass () ((base :reader base :initarg :base :type ) (exponent :reader exponent :initarg :exponent :type ))) (defmethod eqal ((expression-a ) (expression-b )) (and (eqal (base expression-a) (base expression-b)) (eqal (exponent expression-a) (exponent expression-b)))) @export (defclass () ((exponent :reader exponent :initarg :exponent :type ))) (defmethod base ((exp )) (make-instance ' :name :e :value 2.71828)) #+END_SRC *** WORKING Logarithmics :PROPERTIES: :ID: 7d4a23af-6c6b-4ae3-a8af-e477cbee9082 :END: ~~ and ~~ are very much like the above definition of ~~ and ~~, with similar conditions for ~eqal~, the only difference is semantic, being that logarithmic expressions are evaluated differently. #+Caption: Logarithmics #+Name: type-logarithmics #+BEGIN_SRC lisp @export (defclass () ((base :reader base :initarg :base :type ) (log-of :reader log-of :initarg :log-of :type ))) @export (defclass () ((log-of :reader log-of :initarg :log-of :type ))) (defmethod base ((expression )) (make-instance ' :name :e :value 2.7818)) (defmethod eqal ((expression-a ) (expression-b )) (and (eqal (base expression-a) (base expression-b)) (eqal (log-of expression-a) (log-of expression-b)))) #+END_SRC *** WORKING Trigonometric :PROPERTIES: :ID: adbef278-121d-4ef4-a607-7fc129d4d6ea :END: Trigonemtric functions are also a bit weird -- as given a "normal" trig function, /e.g.,/ $\sin{\theta}$, there's the normal, the inverse, the hyperbolic, and the hyperbolic inverse. As such, we have a superclass for each, and then equality, which checks type and ~eqal~-ness of the inner expression. #+Caption: Trig Bases #+Name: type-trigs #+BEGIN_SRC lisp @export (defclass () ((expression :reader expression :initarg :expression :type ))) @export (defclass () ()) @export (defclass () ()) (defmethod eqal ((expression-a ) (expression-b )) (and (equalp (type-of expression-a) (type-of expression-b)) (eqal (expression expression-a) (expression expression-b)))) @export (defclass () ()) @export (defclass () ()) @export (defclass () ()) @export (defclass () ()) @export (defclass () ()) @export (defclass () ()) @export (defclass () ()) @export (defclass () ()) @export (defclass () ()) @export (defclass () ()) @export (defclass () ()) @export (defclass () ()) @export (defclass () ()) @export (defclass () ()) @export (defclass () ()) @export (defclass () ()) @export (defclass () ()) @export (defclass () ()) @export (defclass ( ) ()) @export (defclass ( ) ()) @export (defclass ( ) ()) @export (defclass ( ) ()) @export (defclass ( ) ()) @export (defclass ( ) ()) #+END_SRC ** WORKING Blended Types -- Polynomial Terms :PROPERTIES: :ID: a7258c20-bdd1-4565-9ada-41026ea5e1a0 :END: The ~~ is one of the more interesting types. It's a variable, a multiplication and an exponent, all rolled into one. Therefore, it needs to implement all of the functionality of those. So, while some of the generics are obvious, /i.e.,/ ~exponent~, others are less so. Things like ~value~, ~base~ and ~terms~ must be written directly, and these can later be used, for instance, ~~ can easily be converted to ~~, and an example of this is provided. #+Caption: Polynomial Terms #+Name: type-poly-term #+BEGIN_SRC lisp @export (defclass ( ) ((coefficient :reader coefficient :initarg :coefficient :type ) (variable :reader term-variable :initarg :variable :type ) (exponent :reader exponent :initarg :exponent :type ))) (defmethod value ((expression ) &key resolve-constants) (declare (ignore resolve-constants)) (value (slot-value expression 'variable))) (defmethod base ((expression )) (slot-value expression 'variable)) (defmethod terms ((expression )) (list (slot-value expression 'coefficient) (make-instance ' :base (base expression) :exponent (exponent expression)))) (defmethod update-instance-for-different-class ((prev ) (current ) &key) (setf (slot-value current 'terms) (terms prev))) #+END_SRC ** TODO Object Protocol #+Caption: Object Protocol #+Name: type-obj-proto #+BEGIN_SRC lisp #+END_SRC * WORKING Expression Typing [0/8] :PROPERTIES: :CREATED: <2016-04-30 Sat 23:15> :ID: c6921b1e-d269-4243-acff-5a77685c331e :END: To be able to provide various forms of matching and manipulation, the type of an expression must be determined. This is done by analyzing the contents of the expression. To accomplish this, there must be a way to define a classifier, store all possible classifiers, check a classifier and produce a classification. To provide more flexibility in programming, there is also a special version of case, called ~classification-case~ and a when-pattern macro called ~when-classified-as~. ** TODO Define Classification :PROPERTIES: :CREATED: <2016-05-02 Mon 13:56> :ID: d8826a51-50b8-467a-9e52-158502bd4138 :END: Classifications are defined as ~define-classification~. This macro takes a ~name~, which is the name of the classification, and a body, which is classified within a function. Inside the function, the following are bound: ~expression~, the expression to be classified; and, ~length~, which is the length of the expression if it's a list, otherwise, 0 if it's atomic. A cons cell containing the name of the classification and the name of the classifier is pushed onto classification storage, and the classifier name is exported. #+Caption: Define Classification #+Name: et-define-classification #+BEGIN_SRC lisp (defmacro define-classification (name &body body) (check-type name symbol) (let ((classifier-name (symbolicate name '-classifier))) `(progn (defun ,classifier-name (expression &aux (length (if (listp expression) (length expression) 0))) (declare (ignorable length)) ,@body) (pushnew '(,name . ,classifier-name) *classifications*) (export ',name) ',name))) #+END_SRC ** TODO Check Classification :PROPERTIES: :CREATED: <2016-05-02 Mon 13:56> :ID: 6505b0b1-ffd8-4dd6-b81a-3e49483d8437 :END: To classify an expression, the expression and name of the possible classification is passed in. If the given name of the classification is ~*~, then ~t~ is returned, as this is a catch all; otherwise the classification is retrieved by name, and the expression is passed to the classifier, which will return either ~t~ or ~nil~. #+Caption: Check Classification #+Name: et-check-classification #+BEGIN_SRC lisp (defun classified-as-p (expression classification) (if (eq '* classification) t (funcall (cdr (assoc classification *classifications*)) expression))) #+END_SRC ** TODO Classify Expression :PROPERTIES: :CREATED: <2016-05-02 Mon 14:09> :ID: 82d75d54-1d33-400b-86a3-7d16af938ac8 :END: While being able to check if an expression is given a specific classification is vital, for some things, being able to see what all possible classifications for an expression are can be quite useful. To do this, an expression is passed in, and for each possible classification in the classification storage, it is checked to see whether or not the classification is possible. If it is, the classification is pushed on to a list of valid classifications. When the possible classifications are exhausted, the list of valid classifications is reversed and returned. #+Caption: Classify Expression #+Name: et-classify-expression #+BEGIN_SRC lisp (defun classify (expression) (let ((classifications '())) (dolist (possible ,*classifications* (reverse classifications)) (let ((name (car possible)) (checker (cdr possible))) (when (funcall checker expression) (push name classifications)))))) #+END_SRC ** TODO Classification Case :PROPERTIES: :CREATED: <2016-05-20 Fri 14:15> :ID: 19a4e467-baa0-47eb-9267-93ff3801b1fd :END: Because case is such a useful tool, and because it provides a way to ensure that an expression doesn't fall through when acting on it, I've written the ~classification-case~ macro. It takes an expression, named ~var~ and a list of cases, in the form of ~(classification body-form-1 body-form-2 body-form-n)~. It transforms the cases, converting them to the form ~((classified-as-p expression 'type) body-form-1 body-form-2 body-form-n)~. It finally expands to a ~cond~ in which ~the-classification~ is bound to the full and complete classification of the passed expression. #+Caption: Classification Case #+Name: et-classification-case #+BEGIN_SRC lisp (defmacro classification-case (var &rest cases) (let ((conditions (map 'list #'(lambda (case) (destructuring-bind (type &body body) case (if (eq type 't) `((classified-as-p ,var '*) ,@body) `((classified-as-p ,var ',type) ,@body)))) cases))) `(let ((the-classification (classify ,var))) (declare (ignorable the-classification)) (cond ,@conditions)))) #+END_SRC ** TODO When Classified :PROPERTIES: :CREATED: <2016-05-30 Mon 18:31> :ID: 5c7c3e0b-9170-48e9-a414-6ac4528f9ac3 :END: Another utility macro is ~when-classified-as~, which takes a ~classification~, an expressiond named ~variable~ and a body. It expands fairly simply to a ~when~ form, with the predicate taking the following form ~(classified-as-p variable 'classification)~, wrapping around the passed in body. #+Caption: When Classified #+Name: et-when-classified #+BEGIN_SRC lisp (defmacro when-classified-as (classification variable &body body) `(when (classified-as-p ,variable ',classification) ,@body)) #+END_SRC ** TODO Classifications [13/13] :PROPERTIES: :CREATED: <2016-05-02 Mon 13:56> :ID: dcce4a6b-1b2d-4638-a82b-0c4917b0698a :END: I define the following classifications: - Numerics :: All numbers - Variables :: Any symbols - Non-atomics :: Anything that isn't simply a number or a variable - Additives :: Expressions that are adding multiple terms - Subtractives :: Expressions subtracting multiple terms - Powers :: Expressions of the form $x^n$, where $x$ is a variable, and $n$ is a numeric. - Exponentials :: Expressions of the form $x^y$ or $e^y$, where $x$ and $y$ are generic expressions, and $e$ is Euler's constant. - Logarithmics :: Expressions of the form of $\ln x$ or $\log_b x$, where $x$ and $b$ are generic expressions. - Rationals :: Expressions of the form $\frac{f(x)}{g(x)}$. - Polynomial Terms :: Any integers, multiplicatives of the form $nx^m$ or powers of the form $x^m$, where $x$ is a variable and $n$ and $m$ are numerics. - Polynomials :: Additives or Subtractives consisting solely of Polynomial Terms. - Trigonometrics :: The trig functions: $\sin$, $\cos$, $\tan$, $\csc$, $\sec$ and $\cot$. #+Caption: Possible Classifications #+Name: et-possible-classifications #+BEGIN_SRC lisp <> <> <> <> <> <> <> <> <> <> <> <> <> #+END_SRC *** DONE Numbers CLOSED: [2016-06-14 Tue 23:58] :PROPERTIES: :CREATED: <2016-05-02 Mon 14:26> :ID: 42081153-7cc5-42ff-a17f-53e171c6d1a7 :END: A number is defined as anything that satisfies the built-in ~numberp~. This includes integers, rationals, floats and complex numbers. #+Caption: Classify Numbers #+Name: et-classify-numbers #+BEGIN_SRC lisp (define-classification numeric (numberp expression)) #+END_SRC *** DONE Variables CLOSED: [2016-06-15 Wed 00:00] :PROPERTIES: :CREATED: <2016-05-02 Mon 14:26> :ID: 4c676754-ef9a-485f-91a2-8f1bd83c7659 :END: Variables are defined as anything that satisfies the Common Lisp predicate, ~symbolp~. #+Caption: Classify Variables #+Name: et-classify-variables #+BEGIN_SRC lisp (define-classification variable (symbolp expression)) #+END_SRC *** DONE Non Atomics CLOSED: [2016-06-15 Wed 00:02] :PROPERTIES: :CREATED: <2016-05-04 Wed 19:52> :ID: 414df063-0be1-4849-8b9f-d71aa828be2a :END: Non-atomic is a classification for anything other than numerics and variables. It is defined as anything that satisfies the predicate ~listp~. #+Caption: Classify Non-Atomics #+Name: et-classify-non-atomics #+BEGIN_SRC lisp (define-classification non-atomic (listp expression)) #+END_SRC *** DONE Additives CLOSED: [2016-06-15 Wed 00:03] :PROPERTIES: :CREATED: <2016-05-02 Mon 14:26> :ID: 736d79dc-f34c-4247-b592-690d7f2fddd9 :END: When an expression is non-atomic, and the first element is the symbol ~+~, it is classified as an additive expression. #+Caption: Classify Additives #+Name: et-classify-additives #+BEGIN_SRC lisp (define-classification additive (when-classified-as non-atomic expression (eq '+ (first expression)))) #+END_SRC *** DONE Subtractive CLOSED: [2016-06-15 Wed 00:06] :PROPERTIES: :CREATED: <2016-05-02 Mon 14:26> :ID: c59d086f-2f49-485a-8f96-57d85e774f60 :END: A non-atomic expression for which the first element is the symbol ~-~ is a subtractive expression. #+Caption: Classify Subtractives #+Name: et-classify-subtractives #+BEGIN_SRC lisp (define-classification subtractive (when-classified-as non-atomic expression (eq '- (first expression)))) #+END_SRC *** DONE Powers CLOSED: [2016-06-15 Wed 00:07] :PROPERTIES: :CREATED: <2016-05-02 Mon 14:27> :ID: cc15dd10-7cc0-4370-9e69-daf903b30ad5 :END: A power is any expression that is non-atomic, the first element is the symbol ~expt~, the second is a variable and the third is a numeric. #+Caption: Classify Powers #+Name: et-classify-powers #+BEGIN_SRC lisp (define-classification power (when-classified-as non-atomic expression (and (eq 'expt (first expression)) (classified-as-p (second expression) 'variable) (classified-as-p (third expression) 'numeric)))) #+END_SRC *** DONE Exponentials CLOSED: [2016-06-15 Wed 00:11] :PROPERTIES: :CREATED: <2016-05-02 Mon 15:04> :ID: a11fdd94-d56c-4749-bb22-dca75159dbcb :END: There are two types of exponentials, natural and non-natural. Natural exponentials are defined as being non-atomic, two elements long, and the first element being ~exp~. Non-natural exponentials are defined similarly, but are three elements long, and the first of which is the symbol ~expt~. #+Caption: Classify Exponentials #+Name: et-classify-exponentials #+BEGIN_SRC lisp (define-classification natural-exponential (when-classified-as non-atomic expression (and (= 2 length) (eq 'exp (first expression))))) (define-classification exponential (when-classified-as non-atomic expression (and (= 3 length) (eq 'expt (first expression))))) #+END_SRC *** DONE Multiplicatives CLOSED: [2016-06-15 Wed 00:12] :PROPERTIES: :CREATED: <2016-05-02 Mon 14:27> :ID: feb85a20-93e3-45a1-be01-9893ecc07c53 :END: A multiplicative expression is non-atomic, of any length, and the first element is the symbol ~*~. #+Caption: Classify Multiplicatives #+Name: et-classify-multiplicatives #+BEGIN_SRC lisp (define-classification multiplicative (when-classified-as non-atomic expression (eq '* (first expression)))) #+END_SRC *** DONE Logarithmics CLOSED: [2016-06-15 Wed 00:14] :PROPERTIES: :CREATED: <2016-05-02 Mon 14:27> :ID: 0b733d75-e1ab-413f-8f8a-6a8a47db409c :END: There are two types of logarithmic classifications, natural and non-natural. Natural logarithmics are non-atomic, two elements long, and the first element is the symbol ~log~. Natural logarithmics are also non-atomic, but they are three elements long, starting with the symbol ~log~. #+Caption: Classify Lograthmics #+Name: et-classify-logarithmics #+BEGIN_SRC lisp (define-classification natural-logarithmic (when-classified-as non-atomic expression (and (= 2 length) (eq 'log (first expression))))) (define-classification logarithmic (when-classified-as non-atomic expression (and (= 3 length) (eq 'log (first expression))))) #+END_SRC *** DONE Rationals CLOSED: [2016-06-15 Wed 00:15] :PROPERTIES: :CREATED: <2016-05-02 Mon 14:28> :ID: a4505a66-c249-4438-a6df-81e21718e23e :END: Rationals are non-atomic, three elements long, and the first element is the symbol ~/~. #+Caption: Classify Rationals #+Name: et-classify-rationals #+BEGIN_SRC lisp (define-classification rational (when-classified-as non-atomic expression (and (= 3 length) (eq '/ (first expression))))) #+END_SRC *** DONE Polynomial Terms CLOSED: [2016-06-15 Wed 00:17] :PROPERTIES: :CREATED: <2016-05-02 Mon 14:28> :ID: 37da52b7-98a0-4a16-8a17-a62fcff2ba59 :END: Polynomials are a compound classification: - Numerics - Variables - Powers - Multiplicatives that are a numeric and a variable - Multiplicatives that are a numeric and a power #+Caption: Classify Polynomial Term #+Name: et-classify-polynomial-term #+BEGIN_SRC lisp (define-classification polynomial-term (or (classified-as-p expression 'numeric) (classified-as-p expression 'variable) (classified-as-p expression 'power) (and (classified-as-p expression 'multiplicative) (= (length (rest expression)) 2) (or (and (classified-as-p (second expression) 'numeric) (or (classified-as-p (third expression) 'power) (classified-as-p (third expression) 'variable))) (and (classified-as-p (third expression) 'numeric) (or (classified-as-p (second expression) 'power) (classified-as-p (second expression) 'variable))))))) #+END_SRC *** DONE Polynomials CLOSED: [2016-06-15 Wed 00:19] :PROPERTIES: :CREATED: <2016-05-02 Mon 14:28> :ID: 8cd9045b-81dd-4571-930a-a852f81969c9 :END: Polynomials are compound classifications that are defined as expressions which are either additive or subtrative, for which each term is a polynomial term. #+Caption: Classify Polynomials #+Name: et-classify-polynomials #+BEGIN_SRC lisp (define-classification polynomial (when-classified-as non-atomic expression (and (or (eq '- (first expression)) (eq '+ (first expression))) (reduce #'(lambda (a b) (and a b)) (map 'list #'(lambda (the-expression) (classified-as-p the-expression 'polynomial-term)) (rest expression)))))) #+END_SRC *** DONE Trigonometrics CLOSED: [2016-06-15 Wed 00:22] :PROPERTIES: :CREATED: <2016-05-04 Wed 13:38> :ID: 6f433cad-4b81-4a6f-ab65-981f4a924812 :END: Trigonometrics are defined as non atomic expressions that are two elements long, for which the first element of the expression is either ~sin~, ~cos~, ~tan~, ~csc~, ~sec~, or ~cot~. For each of these there is a classification seperate from the generic ~trigonometric~ classification. #+Caption: Classify Trigonometrics #+Name: et-classify-trigonometrics #+BEGIN_SRC lisp (define-classification trigonometric (when-classified-as non-atomic expression (member (first expression) '(sin cos tan csc sec cot)))) (define-classification sin (when-classified-as non-atomic expression (eq 'sin (first expression)))) (define-classification cos (when-classified-as non-atomic expression (eq 'cos (first expression)))) (define-classification tan (when-classified-as non-atomic expression (eq 'tan (first expression)))) (define-classification csc (when-classified-as non-atomic expression (eq 'csc (first expression)))) (define-classification sec (when-classified-as non-atomic expression (eq 'sec (first expression)))) (define-classification cot (when (classified-as-p expression 'non-atomic) (eq 'cot (first expression)))) #+END_SRC ** TODO Classification Storage :PROPERTIES: :CREATED: <2016-05-02 Mon 13:55> :ID: ff35cd33-3c10-4a45-a2c5-32bc3fdc1acc :END: Classifications are stored in an alist, with the key being the name of the classification, and the value being the classifier itself. These cons cells are stored in the ~*classifications*~ variable. #+Caption: Classification Storage #+Name: et-classification-storage #+BEGIN_SRC lisp (defvar *classifications* '()) #+END_SRC ** TODO Assembly :PROPERTIES: :CREATED: <2016-06-14 Tue 16:59> :ID: bb1d3eb5-b9bf-4378-9716-87ab57dcc8a3 :END: This assembles the classification library, which in the ~#:larcs.classify~ package. It correctly resolves the order of the code, taking it from simple blocks to a complete file. #+Caption: Expression Typing Assembly #+Name: et-assembly #+BEGIN_SRC lisp :tangle "larcs-classify.lisp" (in-package #:larcs.classify) <> <> <> <> <> <> <> #+END_SRC * WORKING Symbolic Differentiation [0/4] :PROPERTIES: :CREATED: <2016-06-13 Mon 22:45> :ID: 552f402a-a25d-4f28-94af-17934c38a529 :END: While this isn't exactly /algebra/, differentiation is important mathematically. This is done rather simply using rules to rewrite an initial expression forming the derivative. ** WORKING Rule Definition [0/3] CLOSED: [2016-08-16 Tue 22:10] :PROPERTIES: :CREATED: <2016-06-13 Mon 22:51> :END: Rule definition is how this system works. This is done rather simply, and is comprised of definition, retrieval and storage portions. *** TODO Definition :PROPERTIES: :CREATED: <2016-06-13 Mon 22:51> :ID: de915ee7-47bd-4f7f-ad06-39f0201a4651 :END: Rules are defined using the ~define-derivative~ macro, which takes an expression type, an arguments list, and a body. If the expression type is not already in the expansions map, it pushes the expression type and expansion name onto the the mapping. Following that, it defines a function for the expansion, using the given arguments list as the lambda-list and the given body for the body for the function. #+Caption: Rule Definition #+Name: sd-rule-definition #+BEGIN_SRC lisp (defmacro define-derivative (expression-type (&rest arguments-list) &body body) (let ((expansion-name (symbolicate expression-type '-expansion))) `(progn (when (not (member ',expression-type (mapcar #'car *rules*))) (setq *rules* (append *rules* '((,expression-type . ,expansion-name))))) (defun ,expansion-name (,@arguments-list) ,@body)))) #+END_SRC *** TODO Retrieval :PROPERTIES: :CREATED: <2016-06-13 Mon 23:08> :ID: 97d8b24e-dd75-4919-a953-cba8035cb691 :END: Rule retrieval works by matching a rewrite rule to an expression by classification. This is done by iterating through the possible classification-rewrite rule pairs, and if the expression matches the classification, returing the rewrite rule to be used. #+Caption: Rule Retrieval #+Name: sd-rule-retrieval #+BEGIN_SRC lisp (defun get-rule (expression) (cdr (first (remove-if #'(lambda (pair) (let ((type (first pair))) (not (classified-as-p expression type)))) ,*rules*)))) #+END_SRC *** TODO Storage :PROPERTIES: :CREATED: <2016-06-13 Mon 22:52> :ID: 372dc2d7-ee67-4eba-a9f7-3633eaf0996e :END: Rules are stored rather simply, in a list of cons cells, with the ~CAR~ being the classification, and the ~CDR~ being the actualy rewrite function. They are found in the variable ~*rules*~. #+Caption: Rule Storage #+Name: sd-rule-storage #+BEGIN_SRC lisp (defvar *rules* '()) #+END_SRC ** WORKING Rules [0/9] :PROPERTIES: :CREATED: <2016-06-13 Mon 22:52> :ID: fdcebadd-b53d-4f59-99a4-4a3782e017a2 :END: The process of symbolic derivation is carried out through rules. These are defined in the following sections, taking apart the equations and putting them back together again as their derivatives. #+Caption: Rules #+Name: sd-rules #+BEGIN_SRC lisp <> <> <> <> <> <> <> <> <> #+END_SRC *** TODO Numbers :PROPERTIES: :CREATED: <2016-06-13 Mon 23:18> :ID: bb1f9175-2e86-43a3-94b3-9467d233539c :END: Numbers are perhaps one of the simplest rules to define, ignore everything and simply return 0, which is, by definition, the derivative of any bare number. #+Caption: Numbers #+Name: sd-numbers #+BEGIN_SRC lisp (define-derivative numeric (&rest junk) (declare (ignorable junk)) 0) #+END_SRC *** TODO Variables :PROPERTIES: :CREATED: <2016-06-13 Mon 23:19> :ID: ecc17ca3-2989-4908-aded-4b6e20b1855c :END: As with Numbers, Variables are just as simple, if something is simply a bare variable, the derivative of such is 1. #+Caption: Variables #+Name: sd-variables #+BEGIN_SRC lisp (define-derivative variable (&rest junk) (declare (ignorable junk)) 1) #+END_SRC *** TODO Polynomial Terms :PROPERTIES: :CREATED: <2016-06-13 Mon 23:33> :ID: 6ca719d7-b584-4ae6-ae44-23bed186c6e9 :END: A Polynomial Term is a bit more complex than the previous two, the rewrite rule has to be able to get the variable, coefficient and current power of the polynomial term. Given this information, there are three possible cases: - The power is equal to 1. :: The coefficient is returned. - The power is equal to 2. :: The coefficient times the variable (in symbolic form) is returned. - The power is greater than 2. :: This comes in the form of $(nm)x^{^}{(n-1)}$. #+Caption: Polynomial Terms #+Name: sd-polynomial-terms #+BEGIN_SRC lisp (define-derivative polynomial-term (&rest term) (let* ((coefficient (coefficient term)) (variable (term-variable term)) (power (get-power term))) (cond ((= 1 power) coefficient) ((= 2 power) `(* ,(* coefficient power) ,variable)) (t `(* ,(* coefficient power) (expt ,variable ,(1- power))))))) #+END_SRC *** TODO Multiplicatives :PROPERTIES: :CREATED: <2016-06-14 Tue 09:57> :ID: 161906a4-5c14-4a84-bf1d-7fae9e20b14f :END: Differentiation of Multiplicative equations are performed by the product rule. This is defined as $\frac{\mathrm{d}}{\mathrm{d}x} f(x) \cdot g(x) = f(x) \cdot g^{\prime}(x) + f^{\prime}(x) \cdot g(x)$. There are some minor exceptions, if $f(x)$ and $g(x)$ are numeric, then the result is the product of the two; if either $f(x)$ or $g(x)$ is numeric and the other is not, then the numeric is placed in front of the other derivative of the remainder. #+Caption: Multiplicatives #+Name: sd-multiplicatives #+BEGIN_SRC lisp (define-derivative multiplicative (function first &rest rest) (declare (ignore function)) (if (= 1 (length rest)) (let ((second (first rest))) (cond ((and (classified-as-p first 'numeric) (classified-as-p second 'numeric)) (* first second)) ((classified-as-p first 'numeric) `(* ,first ,(differentiate second))) ((classified-as-p second 'numeric) `(* ,second ,(differentiate first))) (t `(+ (* ,first ,(differentiate second)) (* ,second ,(differentiate first)))))) (differentiate `(* ,first (* ,@rest))))) #+END_SRC *** TODO Rationals :PROPERTIES: :CREATED: <2016-06-14 Tue 10:21> :ID: cd681a61-a143-4e02-a6a9-e7b8f9b9c77d :END: This follows the quotient rule, which is defined as $\frac{\mathrm{d}}{\mathrm{d}x} \frac{f(x)}{g(x)} = \frac{f^{\prime}(x)g(x) - f(x)g^{\prime}(x)}{g(x)^2}$. #+Caption: Rational Derivatives #+Name: sd-rationals #+BEGIN_SRC lisp (define-derivative rational (function numerator denominator) (declare (ignore function)) `(/ (- (* ,numerator ,(differentiate denominator)) (* ,denominator ,(differentiate numerator))) (expt ,denominator 2))) #+END_SRC *** TODO Additives :PROPERTIES: :CREATED: <2016-06-14 Tue 10:30> :ID: d3a07d51-977c-4b1e-9a63-0eb415977f46 :END: This is quite simple, differentiate each term and add them together. This is accomplished using ~map~. #+Caption: Additives #+Name: sd-additives #+BEGIN_SRC lisp (define-derivative additive (function &rest terms) (declare (ignore function)) `(+ ,@(map 'list #'(lambda (term) (differentiate term)) terms))) #+END_SRC *** TODO Subtractives :PROPERTIES: :CREATED: <2016-06-14 Tue 10:30> :ID: 063f61ee-6fd9-4286-9008-9c80ef0985a5 :END: Following the same pattern as for additives, subtractives map over, deriving each term and prepending the ~-~ symbol to render the derivative of the originally passed subtractive equation. #+Caption: Subtractives #+Name: sd-subtractives #+BEGIN_SRC lisp (define-derivative subtractive (function &rest terms) (declare (ignore function)) `(- ,@(map 'list #'(lambda (term) (differentiate term)) terms))) #+END_SRC *** TODO Exponentials and Logarithmics :PROPERTIES: :CREATED: <2016-06-14 Tue 10:37> :END: There are four types of functions that are supported: - Natural Exponentials :: Effectively this returns itself. - Exponentials :: The original function multiplied by the log of the base. - Natural Logarithmics :: The derivative of the function being passed to $\ln$ over the same function. - Logarithmics :: The derivative of the natural log of the expression over the log of the base times the expression. #+Caption: Exponentials and Logarithms #+Name: sd-exponentials-and-logarithms #+BEGIN_SRC lisp (define-derivative natural-exponential (function expression) (declare (ignore function)) `(exp ,expression)) (define-derivative exponential (function base power) (declare (ignore function)) (if (numberp power) (if (listp base) `(* ,power (expt ,base ,(1- power)) ,(differentiate base)) `(* ,power (expt ,base ,(1- power)))) `(* (expt ,base ,power) (log ,base)))) (define-derivative natural-logarithmic (function expression) (declare (ignore function)) `(/ ,(differentiate expression) ,expression)) (define-derivative logarithmic (function number base) (declare (ignore function)) `(/ ,(differentiate (cons 'log number)) (* (log ,base) ,number))) #+END_SRC *** TODO Trigonometric Functions :PROPERTIES: :CREATED: <2016-06-14 Tue 10:45> :ID: c519d192-5f34-466b-b366-3890208c0928 :END: The following trig functions are supported: - Sine :: $f^{\prime}(x) \cdot \cos(f(x))$ - Cosine :: $^{}f^{\prime}(x) \cdot -\sin(f(x))$ - Tangent :: $f^{\prime}(x) \cdot {\sec(f(x))}^2$ - Cosecant :: $f^{\prime}(x) \cdot \left( \csc(f(x)) - \cot(f(x)) \right)$ - Cotangent :: $f^{\prime}(x) \cdot -{\csc(f(x))}^2$ #+Caption: Trigonometric Functions #+Name: sd-trigonometric-functions #+BEGIN_SRC lisp (define-derivative sin (function expression) (declare (ignore function)) `(* ,(differentiate expression) (cos ,expression))) (define-derivative cos (function expression) (declare (ignore function)) `(* ,(differentiate expression) (- (sin ,expression)))) (define-derivative tan (function expression) (declare (ignore function)) `(* ,(differentiate expression) (expt (sec ,expression) 2))) (define-derivative csc (function expression) (declare (ignore function)) `(* ,(differentiate expression) (- (csc ,expression)) (cot ,expression))) (define-derivative cot (function expression) (declare (ignore function)) `(* ,(differentiate expression) (- (expt (csc ,expression) 2)))) #+END_SRC ** TODO Driver :PROPERTIES: :CREATED: <2016-06-13 Mon 22:59> :ID: b40ed5ad-2eb7-43b1-bab7-39592894e5be :END: This is the derivative driver, ~differentiate~, which in the end is called recursively. It takes an expression (called function), finds a rule using the ~get-rule~ function, and applies the rule to the function, ensuring that the function is passed as a list. #+Caption: Derivative Driver #+Name: sd-derivative-driver #+BEGIN_SRC lisp (defun differentiate (function) (let ((rule (get-rule function))) (when rule (apply rule (ensure-list function))))) #+END_SRC ** TODO Assembly :PROPERTIES: :CREATED: <2016-06-13 Mon 22:46> :ID: d87d49e3-8245-4ff0-aaf0-57b9e19edeba :END: This assembles the package, placing the contents in the correct order and puts them in the file ~larcs-differentiate.lisp~. #+Caption: Symbolic Differentiation #+Name: sd-symbolic-differentiation #+BEGIN_SRC lisp :tangle "larcs-differentiate.lisp" (in-package #:larcs.differentiate) <> <> <> <> <> #+END_SRC * WORKING Library Assembly [0/2] :PROPERTIES: :CREATED: <2016-06-11 Sat 22:30> :ID: 89370949-8f58-41cf-8c4f-92f81d48ac23 :END: LARCS is primarily a library, and as such, the formal interface for the library, and a way to load it must be provided. This is done here by defining packages centrally and defining an ASDF system to load the library quickly and easily. ** WORKING Package Definitions [0/1] :PROPERTIES: :CREATED: <2016-06-13 Mon 15:00> :ID: 573a8352-8cbe-408c-8c27-3cf0b66da885 :END: As all of the packages are defined centrally, this makes resolving inter-package dependencies much easier and convenient. #+Caption: LARCS Packages #+Name: larcs-packages #+BEGIN_SRC lisp :tangle "larcs-packages.lisp" <> #+END_SRC *** TODO Types :PROPERTIES: :ID: 8f14e64c-23a8-461b-9b81-f977deac87ee :END: #+Caption: Types Package #+Name: types-package-def #+BEGIN_SRC lisp (defpackage #:larcs.types (:use #:cl) (:import-from #:annot #:enable-annot-syntax) (:nicknames #:types)) #+END_SRC #+Caption: Types File #+Name: types-file-main #+BEGIN_SRC lisp :tangle "larcs-types-basic.lisp" (in-package #:larcs.types) (enable-annot-syntax) <> <> <> <> <> <> <> <> <> <> <> <> <> <> #+END_SRC ** TODO System Definition :PROPERTIES: :CREATED: <2016-06-13 Mon 15:00> :ID: 35b2ec01-a933-4b5b-af73-b6b7f1c45cb6 :END: This defines the LARCS Library system, allowing the functionality to be loaded by other systems or to provide a way to make development quickly and easily. #+Caption: Library System Definition #+Name: library-system-definition #+BEGIN_SRC lisp :tangle "larcs-lib.asd" (asdf:defsystem #:larcs-lib :description "A CAS Library for use within Lisp software." :author "Samuel W. Flint " :licence "GNU GPLv3 or later" :depends-on (#:alexandria #:cl-annot) :serial t :components ((:file "larcs-packages") (:file "larcs-types-basic"))) #+END_SRC * Push to bottom :ignore: :PROPERTIES: :CREATED: <2016-07-17 Sun 13:58> :END: #+LATEX: \newpage * Version Information :PROPERTIES: :CREATED: <2016-07-17 Sun 13:58> :UNNUMBERED: t :END: This document is version src_sh{git describe --always --long --dirty --abbrev=10 --tags}.