#+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: noweb no-export #+PROPERTY: comments noweb #+LATEX_HEADER: \usepackage[margins=0.75in]{geometry} #+LATEX_HEADER: \parskip=5pt #+LATEX_HEADER: \parindent=0pt #+LATEX_HEADER: \lstset{texcl=true,breaklines=true,columns=fullflexible,basicstyle=\ttfamily,frame=lines,literate={<=}{$\leq$}1 {>=}{$\geq$}1} #+LATEX_CLASS_OPTIONS: [10pt,twoside] #+LATEX_HEADER: \pagestyle{headings} * 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 * WORKING Introduction :nonum: :PROPERTIES: :CREATED: <2016-06-09 Thu 09:19> :END: It's a bold move to do what this does, building a Computer Algebra System from scratch, but I'm doing it anyway. I've chosen to do this because I wanted to understand how most CASs work, and that can be accomplished by either reading thhe source code for one, or by building one. While there are several very good CASs, the majority of them are non-free, and thus I'm not able to learn how exactly they work. Those that are free software are either not complete, or are too complex to be able to learn from easily. This is my Computer Algebra System, and it contains the following components: - [[id:b2c1fd45-b631-48f9-a093-66e1a0faa77f][Algebraic Manipulation]] - [[id:360bc5f4-39ac-4161-9326-00c3daaf368c][Derivation]] - [[id:ed9f4311-bf9f-42df-8f46-254658b93c10][Lisp Equation Conversion to LaTeX]] * DONE What's in a name? :nonum: CLOSED: [2016-06-09 Thu 12:48] :PROPERTIES: :CREATED: <2016-06-09 Thu 12:37> :END: The CAS contained in this is called LARCS, or the Lisp Automated Rewrite and Calculation System. This describes the system as follows: - Lisp :: The CAS is written in Lisp. This is not novel, as other CAS have been written in Lisp before (Macsyma/Maxima), but it is unusual in that most new ones have been written in other languages. - Automated :: The CAS will perform rewrites and calculations automatically. - Rewrite :: The system is built on the concept of a rewrite system. This workse because to perform many actions in the algebra, you rewrite an equation in one way or another. - Calculation :: The ability to go from a symbolic equation, something like $3x^3 + x^2 + 10x - 3$ (~(+ (* 3 (expt x 3)) (expt x 2) (* 10 x) -3)~), to the result where $x \gets 4$ (245). - System :: A complete library and application for symbolic algebra. * TOC :ignore: :PROPERTIES: :CREATED: <2016-06-09 Thu 09:19> :END: #+TOC: headlines 3 #+TOC: listings * TODO Common Functionality :PROPERTIES: :CREATED: <2016-06-11 Sat 22:23> :END: * WORKING Algebraic Manipulation [3/7] :PROPERTIES: :CREATED: <2016-06-09 Thu 09:20> :ID: b2c1fd45-b631-48f9-a093-66e1a0faa77f :END: At the core of LARCS is the algebraic manipulation library. This provides a way for other libraries to add, subtract, multiply and divide symbolically, essentially giving a programmer the ability to create or manipulate equations. While it is neither a solver nor a simplifier, it provides the base for both of them by providing manipulators and automatic expression rewriters. ** DONE Expression Typing [7/7] :PROPERTIES: :CREATED: <2016-04-30 Sat 23:15> :ID: c6921b1e-d269-4243-acff-5a77685c331e :END: To accomplish the goal of providing a complete system to manipulate algebraic expressions, a way to determine the type of expression is important. This will allow for a form of "generic programming" to be used in the development of the manipulator functions, as a way to ensure that the correct manipulator is chosen. This includes a form of storage, the classification definition macro, a way to check a classification, an expression classifier, and all possible classifications. #+Caption: Determine Expression Type #+Name: am-determine-expression-type #+BEGIN_SRC lisp <> <> <> <> <> <> <> #+END_SRC *** DONE Define Classification CLOSED: [2016-05-04 Wed 19:30] :PROPERTIES: :CREATED: <2016-05-02 Mon 13:56> :ID: d8826a51-50b8-467a-9e52-158502bd4138 :END: This is the classification definition macro, ~define-classification~. It takes one symbol argument, ~name~ (the name of the classification), and a body, which is encapsulated within a defun, and binds the following variables: - ~expression~ :: the expression which is to be classified - ~length~ :: the length of the expression if the expression is a list, or 0 if it is not. Aside from defining the classification, it also pushes the classification name and the classifier onto the stack, which can be used for direct classification checking or to completely classify an expression. #+Caption: Define Classification #+Name: am-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*) ',name))) #+END_SRC *** DONE Check Classification CLOSED: [2016-05-04 Wed 19:37] :PROPERTIES: :CREATED: <2016-05-02 Mon 13:56> :ID: 6505b0b1-ffd8-4dd6-b81a-3e49483d8437 :END: To check a classification, the classifier is obtained, unless the specified classifier is ~*~, in which case, ~t~ is always returned. If the classification is not, the classifier function is called on the expression, the result of which is returned. #+Caption: Check Classification #+Name: am-check-classification #+BEGIN_SRC lisp (defun classified-as-p (expression classification) (if (eq '* classification) t (funcall (cdr (assoc classification *classifications*)) expression))) #+END_SRC *** DONE Classify Expression CLOSED: [2016-05-04 Wed 19:44] :PROPERTIES: :CREATED: <2016-05-02 Mon 14:09> :ID: 82d75d54-1d33-400b-86a3-7d16af938ac8 :END: To completely classify an expression, the ~*classifications*~ alist is mapped over, checking to see if each classification is applicable to the expression, if so, the name being returned, otherwise ~nil~. All nils are removed, leaving the complete classification, which is returned for use. #+Caption: Classify Expression #+Name: am-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 *** DONE Classification Case CLOSED: [2016-05-30 Mon 18:17] :PROPERTIES: :CREATED: <2016-05-20 Fri 14:15> :ID: 19a4e467-baa0-47eb-9267-93ff3801b1fd :END: Following the case pattern, and to allow for cleaner code, I've defined the classification case macro. It does this by taking a variable name and a list of cases. These are then mapped over, producing clauses suitable for a ~cond~ expression, to which this macro finally expands, binding the complete classification of the given expression to ~the-classification~. #+Caption: Classification Case #+Name: am-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 *** DONE When Classified CLOSED: [2016-05-30 Mon 19:18] :PROPERTIES: :CREATED: <2016-05-30 Mon 18:31> :ID: 5c7c3e0b-9170-48e9-a414-6ac4528f9ac3 :END: The ~when-classified-as~ macro takes a classification, variable and a body. It expands to a ~when~ form, with the classification and variable put into a ~classified-as-p~ call becoming the predicate, determining whether or not the body is run. #+Caption: When Classified #+Name: am-when-classified #+BEGIN_SRC lisp (defmacro when-classified-as (classification variable &body body) `(when (classified-as-p ,variable ',classification) ,@body)) #+END_SRC *** DONE Classifications [13/13] :PROPERTIES: :CREATED: <2016-05-02 Mon 13:56> :ID: dcce4a6b-1b2d-4638-a82b-0c4917b0698a :END: I must define several different classifications, ranging from simple numeric expressions (numbers) to trigonometric expressions ($\sin$, $\cos$ and the lot). They are as follows: - Numbers - Variables - Non-Atomics - Additives - Subtractives - Powers - Exponentials - Multiplicatives - Logarithmics - Rationals - Polynomial Terms - Polynomials - Trigonometrics #+Caption: Possible Classifications #+Name: am-possible-classifications #+BEGIN_SRC lisp <> <> <> <> <> <> <> <> <> <> <> <> <> #+END_SRC **** DONE Numbers CLOSED: [2016-05-04 Wed 19:56] :PROPERTIES: :CREATED: <2016-05-02 Mon 14:26> :ID: 42081153-7cc5-42ff-a17f-53e171c6d1a7 :END: Check to see if a given expression is a number using ~numberp~. #+Caption: Classify Numbers #+Name: am-classify-numbers #+BEGIN_SRC lisp (define-classification numeric (numberp expression)) #+END_SRC **** DONE Variables CLOSED: [2016-05-04 Wed 19:57] :PROPERTIES: :CREATED: <2016-05-02 Mon 14:26> :ID: 4c676754-ef9a-485f-91a2-8f1bd83c7659 :END: Check to see if a given expression is a variable, that is to say a symbol, using ~symbolp~. #+Caption: Classify Variables #+Name: am-classify-variables #+BEGIN_SRC lisp (define-classification variable (symbolp expression)) #+END_SRC **** DONE Non Atomics CLOSED: [2016-05-04 Wed 19:59] :PROPERTIES: :CREATED: <2016-05-04 Wed 19:52> :ID: 414df063-0be1-4849-8b9f-d71aa828be2a :END: Check to see if a given expression is a non-atomic (any expression other than a number or a variable) using ~listp~. #+Caption: Classify Non-Atomics #+Name: am-classify-non-atomics #+BEGIN_SRC lisp (define-classification non-atomic (listp expression)) #+END_SRC **** DONE Additives CLOSED: [2016-05-04 Wed 20:01] :PROPERTIES: :CREATED: <2016-05-02 Mon 14:26> :ID: 736d79dc-f34c-4247-b592-690d7f2fddd9 :END: Check to see whether or not an expression is an additive by ensuring that it is non-atomic and the first element is the symbol ~+~. #+Caption: Classify Additives #+Name: am-classify-additives #+BEGIN_SRC lisp (define-classification additive (when-classified-as non-atomic expression (eq '+ (first expression)))) #+END_SRC **** DONE Subtractive CLOSED: [2016-05-04 Wed 20:02] :PROPERTIES: :CREATED: <2016-05-02 Mon 14:26> :ID: c59d086f-2f49-485a-8f96-57d85e774f60 :END: Check to see whether a given expression is a subtractive by ensuring it is non-atomic and the first element is the symbol ~-~. #+Caption: Classify Subtractives #+Name: am-classify-subtractives #+BEGIN_SRC lisp (define-classification subtractive (when-classified-as non-atomic expression (eq '- (first expression)))) #+END_SRC **** DONE Powers CLOSED: [2016-05-04 Wed 20:07] :PROPERTIES: :CREATED: <2016-05-02 Mon 14:27> :ID: cc15dd10-7cc0-4370-9e69-daf903b30ad5 :END: This is used to classify "powers", that is to say, equations of the form $x^n$, where $n$ is any numeric. It does so by first ensuring that the expression is non-atomic, following that, it checks to see if the first element in the expression is the symbol ~expt~, the second is a variable and the third a numeric. #+Caption: Classify Powers #+Name: am-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-05-30 Mon 18:24] :PROPERTIES: :CREATED: <2016-05-02 Mon 15:04> :ID: a11fdd94-d56c-4749-bb22-dca75159dbcb :END: This classifies both natural and non-natural exponentials. It does so by ensuring that natural exponentials ($e^x$) are of the form ~(exp x)~, and non-natural exponentials ($a^x$) are of the form ~(expt base power)~. #+Caption: Classify Exponentials #+Name: am-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-05-30 Mon 18:55] :PROPERTIES: :CREATED: <2016-05-02 Mon 14:27> :ID: feb85a20-93e3-45a1-be01-9893ecc07c53 :END: To classify multiplicative expressions, it is first ensured that they are non-atomic, and then, the first element is tested to see if it is equal to the symbol ~*~. #+Caption: Classify Multiplicatives #+Name: am-classify-multiplicatives #+BEGIN_SRC lisp (define-classification multiplicative (when-classified-as non-atomic expression (eq '* (first expression)))) #+END_SRC **** DONE Logarithmics CLOSED: [2016-05-30 Mon 18:30] :PROPERTIES: :CREATED: <2016-05-02 Mon 14:27> :ID: 0b733d75-e1ab-413f-8f8a-6a8a47db409c :END: This defines the classifications for logarithmic expressions, for both natural and non-natural bases. For natural bases ($\ln x$), it ensures that expressions are of the form ~(log x)~, and for non-natural bases ($\log_{b}x$) are of the form ~(log expression base-expression)~. #+Caption: Classify Lograthmics #+Name: am-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-05-30 Mon 18:58] :PROPERTIES: :CREATED: <2016-05-02 Mon 14:28> :ID: a4505a66-c249-4438-a6df-81e21718e23e :END: Rationals are classified similarly to multiplicatives, checking to see whether or not they are non-atomic and checking whether or not the first element is ~/~, but rationals are also defined as only having three elements, the operation and two following operands, and thus, the length is also checked. #+Caption: Classify Rationals #+Name: am-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-05-30 Mon 19:13] :PROPERTIES: :CREATED: <2016-05-02 Mon 14:28> :ID: 37da52b7-98a0-4a16-8a17-a62fcff2ba59 :END: To classify a polynomial term, The expression is checked to see if it satisfies one of the following: - Numeric - Variable - Power - Multiplicative that composed of a numeric and a power or variable. #+Caption: Classify Polynomial Term #+Name: am-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-05-08 Sun 16:46] :PROPERTIES: :CREATED: <2016-05-02 Mon 14:28> :ID: 8cd9045b-81dd-4571-930a-a852f81969c9 :END: This determines whether or not a given expression is a polynomial, that is to say it is either ~additive~ or ~subtractive~, and each and every term is classified as ~polynomial-term~, that is to say, a ~numeric~, ~power~, or a ~multiplicative~ consisting of a ~numeric~ followed by a ~power~. #+Caption: Classify Polynomials #+Name: am-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-05-30 Mon 19:15] :PROPERTIES: :CREATED: <2016-05-04 Wed 13:38> :ID: 6f433cad-4b81-4a6f-ab65-981f4a924812 :END: Trigonometrics are classified as many others are, they are first checked to see if they are non-atomic, and then the first element is checked, with the following being valid symbols: - ~sin~ - ~cos~ - ~tan~ - ~csc~ - ~sec~ - ~cot~ #+Caption: Classify Trigonometrics #+Name: am-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 *** DONE Classification Storage CLOSED: [2016-05-04 Wed 19:49] :PROPERTIES: :CREATED: <2016-05-02 Mon 13:55> :ID: ff35cd33-3c10-4a45-a2c5-32bc3fdc1acc :END: The storage of classifications is simple, they are stored as an alist in the form of ~(name . classifier)~, in the list ~*classifications*~. #+Caption: Classification Storage #+Name: am-classification-storage #+BEGIN_SRC lisp (defvar *classifications* '()) #+END_SRC ** DONE Collect Variables CLOSED: [2016-05-31 Tue 18:54] :PROPERTIES: :CREATED: <2016-05-20 Fri 15:15> :ID: 6333322c-e12f-4ef6-8394-2fe219a72836 :END: Variable collection is somewhat important, and to accomplish this, I use a recursive algorithm. An expression is passed to the function, and if the expression is a variable, then the variable is collected and spit out; otherwise, if the expression is non-atomic, it is passed to the function recursively, and the returned variables are then merged into the variables list. Upon termination (no further sub-expressions), all variables are returned. (See Figure [[fig:variable-collection]].) #+Caption: Variable Collection #+Name: variable-collection #+BEGIN_SRC dot :file "imgs/variable-collection.png" :export results :cache yes digraph { start [label = "Start"]; stop [label = "Stop"]; collect [label = "Collect"]; if_var [label = "If Variable", shape = rectangle]; recurse_collect [label = "Iterate, Recurse and Collect Results"]; start -> if_var; if_var -> collect [label = "True"]; collect -> stop; if_var -> recurse_collect [label = "Non-atomic"]; recurse_collect -> start; } #+END_SRC #+Caption: Variable Collection Algorithm #+Name: fig:variable-collection #+ATTR_LATEX: :width 8cm #+RESULTS[e1586dc50921f7ba260f125e7221a978d489bd34]: variable-collection [[file:imgs/variable-collection.png]] #+Caption: Collect Variables #+Name: am-collect-variables #+BEGIN_SRC lisp (defun collect-variables (expression) (let ((variables '())) (flet ((merge-variables (variable) (pushnew variable variables))) (classification-case expression (variable (merge-variables expression)) (non-atomic (map 'list #'(lambda (expr) (dolist (variable (collect-variables expr)) (merge-variables variable))) (rest expression))))) (reverse variables))) #+END_SRC ** WORKING Term Collection :noexport: :PROPERTIES: :CREATED: <2016-04-30 Sat 22:59> :ID: c1856735-914b-4f73-8825-3e5a062113d2 :END: As there are various forms of expressions, and to provide for simplification, there must be a way to collect terms and return them in a way that allows a programmer to select all sub-expressions of a type within a large expression. #+Caption: Collect Terms #+Name: am-collect-terms #+BEGIN_SRC lisp (defun collect-terms (expression &aux (terms (rest expression))) (let ((numerics '()) (variables '()) (additives '()) (subtractives '()) (multiplicatives '()) (polynomial-terms '()) (rationals '()) (powers '()) (natural-exponentials '()) (exponentials '()) (natural-logarithmics '()) (trigonometrics '())) (dolist (term terms) (classification-case term (numeric (pushnew term numerics)) (variable (pushnew term variables)) (power (pushnew term powers)) (additive (pushnew term additives)) (subtractive (pushnew term subtractives)) (polynomial-term (pushnew term polynomial-terms)) (multiplicative (pushnew term multiplicatives)) (rational (pushnew term rationals)) (power (pushnew term powers)) (natural-exponential (pushnew term natural-exponentials)) (exponential (pushnew term exponentials)) (natural-logarithmic (pushnew term natural-logarithmics)) (trigonometric (pushnew term trigonometrics)))) (remove-if #'(lambda (expr) (null (cdr expr))) (list (cons :numerics numerics) (cons :variables variables) (cons :powers powers) (cons :additives additives) (cons :subtractives subtractives) (cons :multiplicatives multiplicatives) (cons :polynomial-terms polynomial-terms) (cons :rationals rationals) (cons :powers powers) (cons :natural-exponentials natural-exponentials) (cons :exponentials exponentials) (cons :natural-logarithmics natural-logarithmics) (cons :trigonometrics trigonometrics))))) #+END_SRC ** WORKING Polynomial Related Functions [0/6] :PROPERTIES: :CREATED: <2016-05-01 Sun 12:29> :ID: 984d0f52-4c52-4bfa-a150-f3289d25bdf1 :END: #+Caption: Polynomial Related Functions #+Name: am-polynomial-related-functions #+BEGIN_SRC lisp <> <> <> <> <> <> #+END_SRC *** TODO Get Coefficient :PROPERTIES: :CREATED: <2016-05-31 Tue 19:08> :ID: cbc927fc-ae5e-46bf-a028-2872b5c31831 :END: #+Caption: Get Coefficient #+Name: am-get-coefficient #+BEGIN_SRC lisp (defun coefficient (term) (when (classified-as-p term 'polynomial-term) (classification-case term (variable 1) (power 1) (multiplicative (second term)) (numeric term)))) #+END_SRC *** TODO Get Term Variables :PROPERTIES: :CREATED: <2016-05-31 Tue 19:08> :ID: 55729698-bd51-48af-ab42-197871c54dbb :END: #+Caption: Get Term Variable #+Name: am-get-term-variable #+BEGIN_SRC lisp (defun term-variable (term) (when (classified-as-p term 'polynomial-term) (classification-case term (power (second term)) (multiplicative (if (listp (third term)) (second (third term)) (third term))) (numeric nil)))) #+END_SRC *** TODO Get Power :PROPERTIES: :CREATED: <2016-05-31 Tue 19:08> :ID: 7d5a10da-bb30-496f-b285-470057a46db0 :END: #+Caption: Get Power #+Name: am-get-power #+BEGIN_SRC lisp (defun get-power (term) (classification-case term (numeric 0) (variable 1) (power (third term)) (multiplicative (if (listp (third term)) (third (third term)) 1)) (* 0))) #+END_SRC *** TODO Same Order :PROPERTIES: :CREATED: <2016-05-31 Tue 19:08> :ID: c56a1496-f4c2-4693-9448-5043570a752f :END: #+Caption: Same Order #+Name: am-same-order #+BEGIN_SRC lisp (defun same-order-p (term-a term-b) (= (get-power term-a) (get-power term-b))) #+END_SRC *** TODO Same Variable :PROPERTIES: :CREATED: <2016-05-31 Tue 19:08> :ID: 3806c97a-12fa-4488-b38c-d9ff3570c139 :END: #+Caption: Same Variable #+Name: am-same-variable #+BEGIN_SRC lisp (defun same-variable-p (term-a term-b) (eq (term-variable term-a) (term-variable term-b))) #+END_SRC *** TODO Is Combinable :PROPERTIES: :CREATED: <2016-05-31 Tue 19:08> :ID: db0410aa-bb12-4933-9be7-1a50d70ae90f :END: #+Caption: Is Combinable #+Name: am-is-combinable #+BEGIN_SRC lisp (defun single-term-combinable-p (term-a term-b) (and (same-order-p term-a term-b) (same-variable-p term-a term-b))) #+END_SRC ** WORKING Expression Manipulators [2/8] :PROPERTIES: :CREATED: <2016-04-30 Sat 22:58> :ID: 4fe60cc1-be66-4d5e-8922-590554d99004 :END: Foo #+Caption: Expression Manipulation #+Name: am-expression-manipulation #+BEGIN_SRC lisp <> <> <> <> <> <> <> <> #+END_SRC *** DONE Manipulator Miscellaneous Functions CLOSED: [2016-05-08 Sun 10:34] :PROPERTIES: :CREATED: <2016-05-03 Tue 15:38> :ID: 20450528-d763-4c14-a085-5ac54d4d4b85 :END: This defines the ~*manipulator-map*~, where the manipulators for various functions are stored, and defines a function to generate an arguments list given a count of arguments. #+Caption: Misc Manipulator Functions #+Name: am-misc-manipulator-functions #+BEGIN_SRC lisp (defvar *manipulator-map* '()) (defun gen-args-list (count) (let ((letters '(a b c d e f g h i j k l m n o p q r s t u v w x y z))) (let ((variables-list '())) (dotimes (i count) (pushnew (symbolicate 'expression- (nth i letters)) variables-list)) (reverse variables-list)))) #+END_SRC *** WORKING Define Expression Manipulator :PROPERTIES: :CREATED: <2016-04-30 Sat 22:57> :ID: 63909972-428d-47f3-9dc3-3e1fb213aa70 :END: #+Caption: Define Expression Manipulator #+Name: am-define-expression-manipulator #+BEGIN_SRC lisp (defmacro define-operation (name arity short) (check-type name symbol) (check-type arity (integer 1 26)) (check-type short symbol) (let* ((args (gen-args-list arity)) (expression-types (map 'list #'(lambda (x) (symbolicate x '-type)) args)) (rules-name (symbolicate '*manipulators- name '*)) (base-manipulator-name (symbolicate name '-manipulator-)) (manipulator-define-name (symbolicate 'define- name '-manipulator)) (is-applicable-name (symbolicate name '-is-applicable-p)) (get-operations-name (symbolicate 'get- name '-manipulators)) (type-check-list (let ((i 0)) (loop for arg in args collect (prog1 `(classified-as-p ,arg (nth ,i types)) (incf i)))))) `(progn (push '(,short . ,name) *manipulator-map*) (defvar ,rules-name '()) (defun ,is-applicable-name (types ,@args) (and ,@type-check-list)) (defun ,get-operations-name (,@args) (remove-if #'null (map 'list #'(lambda (option) (let ((types (car option)) (name (cdr option))) (if (,is-applicable-name types ,@args) name))) ,rules-name))) (defun ,name (,@args) (funcall (first (,get-operations-name ,@args)) ,@args)) (defmacro ,manipulator-define-name ((,@expression-types) &body body) (let ((manipulator-name (symbolicate ',base-manipulator-name ,@expression-types))) `(progn (setf ,',rules-name (append ,',rules-name '(((,,@expression-types) . ,manipulator-name)))) (defun ,manipulator-name ,',args ,@body))))))) #+END_SRC #+Caption: Expression Manipulation Example #+Name: am-ex-manip-example #+BEGIN_SRC lisp :results output raw :exports results :cache yes (defpackage #:manipulator (:use #:cl) (:import-from #:alexandria #:symbolicate) (:export #:manipulate #:classify #:classified-as-p #:classification-case #:collect-variables #:collect-terms)) (load "manipulation") (in-package #:manipulator) (format t "#+Caption: Expression Manipulator Expansion~%#+Name: am-ex-manip-expansion~%#+BEGIN_SRC lisp :exports code~%~a~%#+END_SRC" (macroexpand-1 '(define-operation frobnicate 2 frob))) #+END_SRC #+RESULTS[afda1ba1b7d141e2f3a5f29167d0ac0f2a03c0c9]: am-ex-manip-example #+Caption: Expression Manipulator Expansion #+Name: am-ex-manip-expansion #+BEGIN_SRC lisp :exports code (PROGN (PUSH '(FROB . FROBNICATE) *MANIPULATOR-MAP*) (DEFVAR *MANIPULATORS-FROBNICATE* 'NIL) (DEFUN FROBNICATE-IS-APPLICABLE-P (TYPES EXPRESSION-A EXPRESSION-B) (AND (CLASSIFIED-AS-P EXPRESSION-A (NTH 0 TYPES)) (CLASSIFIED-AS-P EXPRESSION-B (NTH 1 TYPES)))) (DEFUN GET-FROBNICATE-MANIPULATORS (EXPRESSION-A EXPRESSION-B) (REMOVE-IF #'NULL (MAP 'LIST #'(LAMBDA (OPTION) (LET ((TYPES (CAR OPTION)) (NAME (CDR OPTION))) (IF (FROBNICATE-IS-APPLICABLE-P TYPES EXPRESSION-A EXPRESSION-B) NAME))) *MANIPULATORS-FROBNICATE*))) (DEFUN FROBNICATE (EXPRESSION-A EXPRESSION-B) (FUNCALL (FIRST (GET-FROBNICATE-MANIPULATORS EXPRESSION-A EXPRESSION-B)) EXPRESSION-A EXPRESSION-B)) (DEFMACRO DEFINE-FROBNICATE-MANIPULATOR ((EXPRESSION-A-TYPE EXPRESSION-B-TYPE) &BODY BODY) (DECLARE (SLIME-INDENT (AS DEFUN))) (LET ((MANIPULATOR-NAME (SYMBOLICATE 'FROBNICATE-MANIPULATOR- EXPRESSION-A-TYPE EXPRESSION-B-TYPE))) `(PROGN (SETF ,'*MANIPULATORS-FROBNICATE* (APPEND ,'*MANIPULATORS-FROBNICATE* '(((,EXPRESSION-A-TYPE ,EXPRESSION-B-TYPE) ,@MANIPULATOR-NAME)))) (DEFUN ,MANIPULATOR-NAME ,'(EXPRESSION-A EXPRESSION-B) ,@BODY))))) #+END_SRC *** DONE External Manipulator CLOSED: [2016-05-31 Tue 19:48] :PROPERTIES: :CREATED: <2016-05-01 Sun 14:33> :ID: 6419490c-3cb0-47e4-840a-c20af4bfb3d7 :END: The Expression Manipulators should not be touched outside of this package, as they are not designed to be used outside of it. Instead, they should be used through this simple function. It takes an action and a list of expressions. The function used to perform the action correctly is determined, and used to reduce the expressions. #+Caption: External Manipulator #+Name: am-external-manipulator #+BEGIN_SRC lisp (defun manipulate (action &rest expressions) (let ((the-manipulator (cdr (assoc action *manipulator-map*)))) (reduce the-manipulator expressions))) #+END_SRC *** WORKING Addition :PROPERTIES: :CREATED: <2016-04-30 Sat 23:08> :ID: b794486c-e493-408f-b80c-a440edae1bc8 :END: Foo #+Caption: Addition Manipulator #+Name: am-addition-manipulator #+BEGIN_SRC lisp (define-operation add 2 +) (define-add-manipulator (numeric numeric) (+ expression-a expression-b)) (define-add-manipulator (numeric additive) (let ((total expression-a) (remainder (rest expression-b)) (non-numeric '())) (dolist (element remainder) (if (classified-as-p element 'numeric) (incf total element) (push element non-numeric))) (cond ((null non-numeric) total) ((= 0 total) `(+ ,@non-numeric)) (t `(+ ,total ,@non-numeric))))) (define-add-manipulator (additive additive) (let ((total 0) (elements (append (rest expression-a) (rest expression-b))) (non-numeric '())) (dolist (element elements) (if (classified-as-p element 'numeric) (incf total element) (push element non-numeric))) (cond ((null non-numeric) total) ((= 0 total) `(+ ,@non-numeric)) (t `(+ ,total ,@non-numeric))))) (define-add-manipulator (numeric subtractive) (let ((total expression-a) (the-other (rest expression-b)) (non-numeric '())) (dolist (element the-other) (if (classified-as-p element 'numeric) (decf total element) (push element non-numeric))) (cond ((null non-numeric) total) ((= 0 total) `(+ ,@non-numeric)) (t `(+ ,total (-,@non-numeric)))))) (define-add-manipulator (numeric polynomial-term) `(+ ,expression-a ,expression-b)) (define-add-manipulator (polynomial-term polynomial-term) (if (single-term-combinable-p expression-a expression-b) (let ((new-coefficient (+ (coefficient expression-a) (coefficient expression-b))) (variable (term-variable expression-a)) (power (get-power expression-a))) `(* ,new-coefficient (expt ,variable ,power))) `(+ ,expression-a ,expression-b))) (define-add-manipulator (* numeric) (add expression-b expression-a)) #+END_SRC *** WORKING Subtraction :PROPERTIES: :CREATED: <2016-04-30 Sat 23:08> :ID: f675fd81-e995-41ee-9570-cc78261d9dc1 :END: Foo #+Caption: Subtraction Manipulator #+Name: am-subtraction-manipulator #+BEGIN_SRC lisp (define-operation subtract 2 -) (define-subtract-manipulator (numeric numeric) (- expression-a expression-b)) (define-subtract-manipulator (numeric subtractive) (let ((total expression-a) (elements (rest expression-b)) (non-numeric '())) (dolist (element elements) (if (classified-as-p element 'numeric) (decf total element) (push element non-numeric))) (cond ((null non-numeric) total) ((= 0 total) `(- ,@(reverse non-numeric))) (t `(- ,total ,@(reverse non-numeric)))))) (define-subtract-manipulator (* numeric) (subtract expression-b expression-a)) #+END_SRC *** WORKING Multiplication :PROPERTIES: :CREATED: <2016-04-30 Sat 23:08> :ID: cddffdaa-10dd-425f-9697-3f0617162953 :END: Foo #+Caption: Multiplication Manipulators #+Name: am-multiplication-manipulators #+BEGIN_SRC lisp (define-operation multiply 2 *) (define-multiply-manipulator (numeric numeric) (* expression-a expression-b)) (define-multiply-manipulator (numeric polynomial-term) (let ((new-coefficient (* expression-a (coefficient expression-b))) (variable (term-variable expression-b)) (power (get-power expression-b))) (if (= 1 power) `(* ,new-coefficient ,variable) `(* ,new-coefficient (expt ,variable ,power))))) (define-multiply-manipulator (polynomial-term polynomial-term) (let ((new-coefficient (* (coefficient expression-a) (coefficient expression-b))) (variable (term-variable expression-b)) (power (+ (get-power expression-a) (get-power expression-b)))) `(* ,new-coefficient (expt ,variable ,power)))) #+END_SRC *** WORKING Division :PROPERTIES: :CREATED: <2016-04-30 Sat 23:09> :ID: 4c4f7034-555a-46b0-85b9-56a08cf48f9b :END: Foo #+Caption: Division Manipulators #+Name: am-division-manipulators #+BEGIN_SRC lisp (define-operation division 2 /) (define-division-manipulator (numeric numeric) (/ expression-a expression-b)) (define-division-manipulator (polynomial-term polynomial-term) (let ((new-coefficient (/ (coefficient expression-a) (coefficient expression-b))) (variable (term-variable expression-b)) (power (- (get-power expression-a) (get-power expression-b)))) `(* ,new-coefficient (expt ,variable ,power)))) #+END_SRC *** WORKING Trigonometric [0/6] :PROPERTIES: :CREATED: <2016-04-30 Sat 23:09> :ID: ba4acf37-9074-429b-a2c8-a23094e1c86b :END: Foo #+Caption: Trigonometric Manipulators #+Name: am-trigonometric-manipulators #+BEGIN_SRC lisp <> <> <> <> <> <> #+END_SRC **** WORKING Sine :PROPERTIES: :CREATED: <2016-05-08 Sun 16:22> :ID: c733c6b3-a44a-488f-8b6e-38346830b257 :END: Foo #+Caption: Sine Manipulators #+Name: am-sine-manipulators #+BEGIN_SRC lisp (define-operation sine 1 sin) (define-sine-manipulator (numeric) (sin expression-a)) #+END_SRC **** WORKING Cosine :PROPERTIES: :CREATED: <2016-05-08 Sun 16:22> :ID: c2fbd362-6932-4483-8270-e3ad72a308fd :END: Foo #+Caption: Cosine Manipulators #+Name: am-cosine-manipulators #+BEGIN_SRC lisp (define-operation cosine 1 cos) (define-cosine-manipulator (numeric) (cosine expression-a)) #+END_SRC **** WORKING Tangent :PROPERTIES: :CREATED: <2016-05-08 Sun 16:22> :ID: 07222206-1c22-411e-a8ab-13e1a627e9ef :END: Foo #+Caption: Tangent Manipulators #+Name: am-tangent-manipulators #+BEGIN_SRC lisp (define-operation tangent 1 tan) (define-tangent-manipulator (numeric) (tan expression-a)) #+END_SRC **** WORKING Cosecant :PROPERTIES: :CREATED: <2016-05-08 Sun 16:22> :ID: e77c0317-7281-45ff-b86b-8d66fb8c38ef :END: Foo #+Caption: Cosecant Manipulators #+Name: am-cosecant-manipulators #+BEGIN_SRC lisp (define-operation cosecant 1 csc) #+END_SRC **** WORKING Secant :PROPERTIES: :CREATED: <2016-05-08 Sun 16:23> :ID: 6c377c7d-ec84-4fcf-be94-db89b832c2d8 :END: Foo #+Caption: Secant Manipulators #+Name: am-secant-manipulators #+BEGIN_SRC lisp (define-operation secant 1 sec) #+END_SRC **** WORKING Cotangent :PROPERTIES: :CREATED: <2016-05-08 Sun 16:23> :ID: 70a9fb76-7ca7-4c7d-b25b-0fa94d390b6c :END: Foo #+Caption: Cotangent Manipulators #+Name: am-cotangent-manipulators #+BEGIN_SRC lisp (define-operation cotangent 1 cot) #+END_SRC ** DONE Packaging CLOSED: [2016-05-05 Thu 21:21] :PROPERTIES: :CREATED: <2016-04-30 Sat 23:07> :ID: d487ed31-295b-4274-aef2-b45e4fa7bec2 :END: This assembles and packages the algebraic manipulation system into a single file and library. To do so, it must first define a package, import specific symbols from other packages, and export symbols from itself. It then includes the remainder of the functionality, placing it in the file ~manipulation.lisp~. #+Caption: Packaging #+Name: am-packaging #+BEGIN_SRC lisp :tangle "manipulation.lisp" (in-package #:manipulator) <> <> <> <> <> #+END_SRC * WORKING Symbolic Solver [0/3] :PROPERTIES: :CREATED: <2016-06-11 Sat 17:55> :END: ** TODO Techniques :PROPERTIES: :CREATED: <2016-06-11 Sat 17:55> :END: ** TODO Rules :PROPERTIES: :CREATED: <2016-06-11 Sat 17:56> :END: ** TODO Assembly :PROPERTIES: :CREATED: <2016-06-11 Sat 17:56> :END: * WORKING Symbolic Trigonometry [0/2] :PROPERTIES: :CREATED: <2016-06-11 Sat 17:58> :END: ** TODO Rules :PROPERTIES: :CREATED: <2016-06-11 Sat 17:58> :END: ** TODO Assembly :PROPERTIES: :CREATED: <2016-06-11 Sat 17:58> :END: * WORKING Symbolic Differentiation [0/5] :PROPERTIES: :CREATED: <2016-06-09 Thu 09:21> :ID: 360bc5f4-39ac-4161-9326-00c3daaf368c :END: The calculation of derivatives has many uses. However, the calculation of derivatives can often be tedious. To make this faster, I've written the following program to make it faster. ** WORKING Expansions [0/4] CLOSED: [2016-06-09 Thu 09:22] :PROPERTIES: :CREATED: <2016-06-09 Thu 09:22> :END: This program works in terms of expansion functions, and application tests. That is to say, there is a test to see if the expansion is valid for the given expression. *** TODO Match Expressions :PROPERTIES: :ID: 39f69de5-6fcc-4ad4-984f-72fc0f77f11b :CREATED: <2016-06-11 Sat 22:20> :END: To be able to apply an expansion, you need to determine eligibility. To do this, you need an expression that matches on two things, function name and arity. To generate this, it takes an operation name and the arity. Based on the arity type ($=$, $>$, $\leq$), it will construct a simple boolean statement in the format of $(function = operator) \land (argument-count == arity)$, where $==$ is one of the above arity types. #+Caption: Match Expressions #+Name: derive-match-expressions #+BEGIN_SRC lisp (defun generate-match-expression (on arity &optional (type '=)) (check-type on symbol) (check-type type (member = > >=)) (check-type arity (integer 0)) (case type (= `(and (eq function ',on) (= arg-count ,arity))) (> `(and (eq function ',on) (> arg-count ,arity))) (>= `(and (eq function ',on) (>= arg-count ,arity))))) #+END_SRC *** WORKING Definition :PROPERTIES: :ID: d7430ac9-cc9a-4942-a8c7-4d21c1705ad4 :CREATED: <2016-06-11 Sat 22:20> :END: To define an expansion requires just a bit of syntactic sugar in the form of the ~defexpansion~ macro. This macro does 3 things, generate a test function, generate an expansion function and pushes the name of the expansion, the test function and the expansion function on to the rules list. To generate the test function, it uses the match-expression generator and wraps it into a function taking two arguments, a function and a list of arguments to the function. The test is then made, acting as predicate function for whether or not the expansion is applicable. To generate the expansion function, a series of expressions is used as the body of the function, with the function destructured to form the arguments. #+Caption: Expansion Definition #+Name: derive-expansion-definition #+BEGIN_SRC lisp (defmacro defexpansion (name (on arity &optional (type '=)) (&rest arguments) &body expansion) (let ((match-expression (generate-match-expression on arity type)) (test-name (symbolicate name '-test)) (expansion-name (symbolicate name '-expansion))) `(progn (defun ,test-name (function &rest arguments &aux (arg-count (length arguments))) ,match-expression) (defun ,expansion-name (,@arguments) ,@expansion) (setf (aget *rules* ',name) (make-rule :name ',name :test-function #',test-name :expansion-function #',expansion-name)) ',name))) #+END_SRC *** WORKING Retrieval :PROPERTIES: :ID: 71d8545b-d5d1-4179-a0b1-3539c8e68105 :CREATED: <2016-06-11 Sat 22:20> :END: To allow for the use of expansions, you must be able to retrieve the correct one from the expansions list. To do so, you need the second element of the list that is the ~(name test expansion)~ for the rule. This is found by removing the expansions for which the test returns false for the given expression. #+Caption: Expansion Retrieval #+Name: derive-expansion-retrieval #+BEGIN_SRC lisp (defun get-expansion (expression) (rule-expansion-function (rest (first (remove-if-not #'(lambda (nte) (let ((test (rule-test-function (rest nte)))) (apply test expression))) ,*rules*))))) #+END_SRC *** TODO Storage :PROPERTIES: :ID: 0cf2d0ad-cdd1-4a5e-a849-615961c2e869 :CREATED: <2016-06-11 Sat 22:20> :END: One of the more important parts of the program is a way to store expansions. This is however, quite boring. It's just a global variable (~*rules*~), containing a list of lists having the form of ~(name test-lambda expander-lambda)~. #+Caption: Expansion Storage #+Name: derive-expansion-storage #+BEGIN_SRC lisp (defstruct (rule (:type list)) name test-function expansion-function) (defvar *rules* '()) #+END_SRC ** WORKING Rules [0/5] CLOSED: [2016-06-09 Thu 09:22] :PROPERTIES: :CREATED: <2016-06-09 Thu 09:22> :END: There are many rules for derivation of equations. These rules allow one to derive equations quickly and easily by matching equations up with relevant rules and applying those rules. *** TODO Multiplication :PROPERTIES: :ID: 15f0ba68-9335-4d97-b3c7-418187895706 :CREATED: <2016-06-11 Sat 22:21> :END: The derivatives of multiplication follows two rules, the Constant Multiple rule: \[ \frac{d}{dx} cf(x) = c \cdot f^\prime(x) ,\] which is a specialized version of the more generalized Product Rule: \[ \frac{d}{dx} f(x) \cdot g(x) = f(x) \cdot g^\prime(x) + g(x) \cdot f^\prime(x) .\] There are two forms of the Product Rule as implemented, both matching on the ~*~ function, but taking a different number of arguments. The first takes 2 arguments, and is the main driver for derivation, following the two above rules. The second takes 3 or more, and modifies the arguments slightly so as to make it a derivative of two different equations. #+Caption: Rules for Multiplication #+Name: derive-multiplication #+BEGIN_SRC lisp (defexpansion mult/2 (* 2) (first second) (cond ((numberp first) `(* ,first ,(derive (ensure-list second)))) ((numberp second) `(* ,second ,(derive (if (listp first) first (list second))))) (t `(+ (* ,first ,(derive (ensure-list second))) (* ,second ,(derive (ensure-list first))))))) (defexpansion mult/3+ (* 3 >=) (first &rest rest) (derive `(* ,first ,(cons '* rest)))) #+END_SRC *** TODO Division :PROPERTIES: :ID: 483285d3-f035-4b50-9f3f-4389d01b7504 :CREATED: <2016-06-11 Sat 22:21> :END: Division follows the Quotient Rule, which is as follows: \[ \frac{d}{dx} \frac{f(x)}{g(x)} = \frac{f^\prime(x) \cdot g(x) - g^\prime(x) \cdot f(x)}{(g(x))^2} .\] The rule matches on the ~/~ function, and takes 2 arguments, a numerator and a denominator, its expansion is as above. #+Caption: Rules for Division #+Name: derive-division #+BEGIN_SRC lisp (defexpansion div/2 (/ 2) (numerator denominator) `(/ (- (* ,numerator ,(derive (ensure-list denominator))) (* ,denominator ,(derive (ensure-list numerator)))) (expt ,denominator 2))) #+END_SRC *** TODO Addition/Subtraction :PROPERTIES: :ID: b4f6b80a-0904-491a-a0ca-850dcb6809c5 :CREATED: <2016-06-11 Sat 22:21> :END: Addition and subtraction of functions in derivatives is simple, simply add or subtract the derivatives of the functions, as shown here: \[ \frac{d}{dx} f_1(x) + f_2(x) + \cdots + f_n(x) = f_1^\prime(x) + f_2^\prime(x) + \cdots + f_n^\prime(x) \] and here: \[ \frac{d}{dx} f_1(x) - f_2(x) - \cdots - f_n(x) = f_1^\prime(x) - f_2^\prime(x) - \cdots - f_n^\prime(x) .\] This is accomplished by matching on either ~+~ or ~-~, and taking 2 or more arguments, deriving all of the passed in equations and applying the respective operation. #+Caption: Rules for Addition and Subtraction #+Name: derive-addition-subtraction #+BEGIN_SRC lisp (defexpansion plus/2+ (+ 2 >=) (&rest clauses) `(+ ,@(map 'list #'(lambda (clause) (if (listp clause) (derive clause) (derive (list clause)))) clauses))) (defexpansion minus/2+ (- 2 >=) (&rest clauses) `(- ,@(map 'list #'(lambda (clause) (if (listp clause) (derive clause) (derive (list clause)))) clauses))) #+END_SRC *** TODO Exponentials and Logarithms :PROPERTIES: :ID: eaed7558-82d0-4300-8e5f-eb48a06d4e64 :CREATED: <2016-06-11 Sat 22:21> :END: The derivatives of exponential and logarithmic functions follow several rules. For $e^x$ or $a^x$, the "Xerox" rule is used: \[ \frac{d}{dx} e^x = e^x ,\] and \[ \frac{d}{dx} a^x = a^x \cdot \ln x .\] Logarithmic functions follow the forms as shown: \[ \frac{d}{dx} \ln x = \frac{x^\prime}{x} ,\] and \[ \frac{d}{dx} \log_b x = \frac{x^\prime}{\ln b \cdot x} .\] However, equations of the form $x^n$ follow this form (The Power Rule): \[ \frac{d}{dx} x^n = x^\prime \cdot n \cdot x^{n-1} .\] The following rules match based on the appropriate Lisp functions and the number of arguments taken based on whether or not you are performing natural or unnatural operations. #+Caption: Rules for Exponentials and Logarithms #+Name: derive-exponentials-logarithms #+BEGIN_SRC lisp (defexpansion exp/1 (exp 1) (expression) (if (listp expression) `(* (exp ,expression) ,(derive expression)) (if (numberp expression) 0 `(exp ,expression)))) (defexpansion expt/2 (expt 2) (base exponent) (if (numberp exponent) (if (listp base) `(* ,exponent (expt ,base ,(1- exponent)) ,(derive base)) `(* ,exponent (expt ,base ,(1- exponent)))) `(* (expt ,base ,exponent) (log ,base)))) (defexpansion log/1 (log 1) (expression) `(/ ,(derive (ensure-list expression)) ,expression)) (defexpansion log/2 (log 2) (number base) (declare (ignorable number base)) `(/ ,(derive (cons 'log number)) (* (log ,base) ,number))) #+END_SRC *** TODO Trigonometric :PROPERTIES: :ID: c0f40e80-8a19-4749-bc9b-b1e94ef6949a :CREATED: <2016-06-11 Sat 22:21> :END: The derivation of trigonometric functions is simply the application of the chain rule. As such, each of the trig functions has a different derivative, as shown here: \[ \frac{d}{dx} \sin x = x^\prime \cdot \cos x ,\] \[ \frac{d}{dx} \cos x = x^\prime \cdot -\sin x ,\] \[ \frac{d}{dx} \tan x = x^\prime \cdot \sec^2 x ,\] \[ \frac{d}{dx} \csc x = x^\prime \cdot -\csc x \cdot \cot x ,\] \[ \frac{d}{dx} \sec x = x^\prime \cdot \sec x \cdot \tan x ,\] and \[ \frac{d}{dx} \cot x = x^\prime \cdot -\csc^2 x .\] These rules all match on their respective trig function and substitute as appropriate. #+Caption: Rules for Trigonometric Functions #+Name: derive-trigonometrics #+BEGIN_SRC lisp (defexpansion sin/1 (sin 1) (arg) `(* (cos ,arg) ,(derive (ensure-list arg)))) (defexpansion cos/1 (cos 1) (arg) `(* (- (sin ,arg)) ,(derive (ensure-list arg)))) (defexpansion tan/1 (tan 1) (arg) `(* (expt (sec ,arg) 2) ,(derive (ensure-list arg)))) (defexpansion csc/1 (csc 1) (arg) `(* (- (csc ,arg)) (cot ,arg) ,(derive (ensure-list arg)))) (defexpansion sec/1 (sec 1) (arg) `(* (sec ,arg) (tan ,arg) ,(derive (ensure-list arg)))) (defexpansion cot/1 (cot 1) (arg) `(* (- (expt (csc ,arg) 2)) ,(derive (ensure-list arg)))) #+END_SRC ** TODO Derivative Driver :PROPERTIES: :ID: b03c5070-602a-412e-a6ce-3dda65630153 :CREATED: <2016-06-09 Thu 09:22> :END: This function is probably the most important user-facing function in the package. Derive takes a list, and based on the first element in the list, and the length of the list, it will do one of the following things: - Number :: Return 0, the derivative of a number is 0, except in certain cases listed above. - Symbol, and length is 1 :: This is a variable. Return 1, $\frac{d}{dx}x=1$. - Expansion Function Available :: There is an expansion rule, use this to derive the equation. - No Expansion Rule :: Signal an error, equation was likely malformed. #+Caption: Derivative Driver #+Name: derive-derivative-driver #+BEGIN_SRC lisp (defun derive (function) (check-type function cons) (let ((op (first function))) (cond ((numberp op) 0) ((and (symbolp op) (= 1 (length function))) 1) (t (let ((expansion-function (get-expansion function))) (if (functionp expansion-function) (apply expansion-function (rest function)) (error "Undefined expansion: ~a" op))))))) #+END_SRC ** TODO Miscellaneous Functions :PROPERTIES: :ID: 41439f82-466f-46a5-b706-df43e5f23650 :CREATED: <2016-06-09 Thu 09:22> :END: As Common Lisp does not have cosecant or secant functions, and they appear in the definitions of the derivatives of some trigonometric functions, I define them here as follows: \[ \csc x = \frac{1}{\sin x} \] \[ \sec x = \frac{1}{\cos x} \] I also take the liberty of defining two macros, a ~define-equation-functions~ macro and ~take-derivative~. The first defines two functions, one that is the original equation, and the second being the derivative of the original equation. The ~take-derivative~ macro does simply that, but allows you to write the equation without having to quote it, providing a little bit of syntactic sugar. #+Caption: Miscellaneous Functions #+Name: derive-misc-functions #+BEGIN_SRC lisp (defun csc (x) "csc -- (csc x) Calculate the cosecant of x" (/ (sin x))) (defun sec (x) "sec -- (sec x) Calculate the secant of x" (/ (cos x))) (defmacro define-equation-functions (name variable equation) (let ((derivative-name (symbolicate 'd/d- variable '- name)) (derivative (derive equation))) `(progn (defun ,name (,variable) ,equation) (defun ,derivative-name (,variable) ,derivative)))) (defmacro take-derivative (equation) (let ((derivative (derive equation))) `',derivative)) #+END_SRC ** TODO Packaging :PROPERTIES: :ID: e15262d2-23d5-4306-a68b-387a21265b6e :CREATED: <2016-06-09 Thu 09:22> :END: Now that the functions, macros and rules are defined, it's time to put them together into a package. This package has only one dependency, Common Lisp itself, and exports the following five symbols: ~derive~, ~csc~, ~sec~, ~define-equation-functions~ and ~take-derivative~. #+Caption: Packaging #+Name: derive-packaging #+BEGIN_SRC lisp :tangle "derive.lisp" (in-package #:derive) <> <> <> <> <> <> <> <> <> <> <> #+END_SRC * WORKING Symbolic Integration [0/3] :PROPERTIES: :CREATED: <2016-06-11 Sat 18:02> :END: ** TODO Rules :PROPERTIES: :CREATED: <2016-06-11 Sat 18:02> :END: ** TODO Techniques :PROPERTIES: :CREATED: <2016-06-11 Sat 18:02> :END: ** TODO Assembly :PROPERTIES: :CREATED: <2016-06-11 Sat 18:02> :END: * WORKING Conversion from Symbolic Expressions to Typeset Display Formats [0/5] :PROPERTIES: :CREATED: <2016-06-09 Thu 09:23> :ID: ed9f4311-bf9f-42df-8f46-254658b93c10 :END: The goal of this portion of the CAS is to produce \LaTeX{} formulae that can be inserted into a document for whatever reason, and it does so using rewrite rules, this time, rewriting s-expressions (~(+ (* 3 (expt x 3)) (expt x 2) (* 4 x) 22)~) to the \LaTeX{} equivalent, ~${{{{3} \cdot {{x ^ {3}}}}} + {{x ^ {2}}} + {{{4} \cdot {x}}} + {22}}$~ (${{{{3} \cdot {{x ^ {3}}}}} + {{x ^ {2}}} + {{{4} \cdot {x}}} + {22}}$). ** WORKING Matching And Generating [0/4] :PROPERTIES: :CREATED: <2016-04-30 Sat 16:19> :END: *** TODO Match Test :PROPERTIES: :ID: 9d165cb9-95f2-4006-a8a1-73a0750b2000 :CREATED: <2016-04-30 Sat 16:19> :END: #+Caption: Generate Match Test #+Name: tex-gen-match-test #+BEGIN_SRC lisp (defun generate-match-expression (op arity &optional (type '=)) (declare (symbol op type) (integer arity)) (ecase type (= `(and (eq function ',op) (= arg-count ,arity))) (> `(and (eq function ',op) (> arg-count ,arity))) (>= `(and (eq function ',op) (>= arg-count ,arity))))) #+END_SRC *** TODO Define Rule :PROPERTIES: :ID: d4f77ac3-a059-4fb6-b936-1b9e972646ee :CREATED: <2016-04-30 Sat 16:19> :END: #+Caption: Define Matching Rule #+Name: tex-def-match-rule #+BEGIN_SRC lisp (defmacro defrule (name (on arity &optional type) (&rest arguments) &body rule) (let ((match-expression (generate-match-expression on arity type)) (test-name (symbolicate name '-test)) (expansion-name (symbolicate name '-expansion))) `(progn (defun ,test-name (function &rest arguments &aux (arg-count (length arguments))) ,match-expression) (defun ,expansion-name (,@arguments) ,@rule) (setf (aget *rules* ',name) (make-rule :name ',name :test-function #',test-name :expansion-function #',expansion-name)) ',name))) #+END_SRC *** TODO Store Rules :PROPERTIES: :ID: 002ea704-4286-429f-9149-0f29fb73c503 :CREATED: <2016-04-30 Sat 16:19> :END: #+Caption: Rule Storage #+Name: tex-rule-storage #+BEGIN_SRC lisp (defstruct (rule (:type list)) name test-function expansion-function) (defvar *rules* '()) #+END_SRC *** TODO Retrieve Rule :PROPERTIES: :CREATED: <2016-04-30 Sat 15:25> :ID: e3f34100-d0a5-4039-8b9d-115cfcb0804e :END: #+Caption: Retrieve Rule #+Name: tex-retrieve-rule #+BEGIN_SRC lisp (defun get-expansion (expression) (rule-expansion-function (rest (first (remove-if-not #'(lambda (nte) (let ((test (rule-test-function (rest nte)))) (apply test expression))) ,*rules*))))) #+END_SRC ** WORKING Rules [0/10] :PROPERTIES: :CREATED: <2016-04-30 Sat 16:19> :END: *** TODO Multiplication :PROPERTIES: :CREATED: <2016-04-30 Sat 16:19> :ID: 5417a6bf-f265-418a-984b-6bfd14b79a80 :END: #+Caption: Multiplication Rule #+Name: tex-multiplication-rule #+BEGIN_SRC lisp (defrule multiplication (* 2 >=) (&rest elements) (format nil "{~{{~a}~^ \\cdot ~}}" (map 'list #'convert-to-tex (map 'list #'ensure-list elements)))) #+END_SRC *** TODO Division :PROPERTIES: :CREATED: <2016-04-30 Sat 16:19> :ID: 056aa99c-f2b9-4ab6-99ba-bfb87e3baed5 :END: #+Caption: Division Rule #+Name: tex-division-rule #+BEGIN_SRC lisp (defrule division (/ 2 =) (a b) (format nil "{\\frac{~a}{~a}}" (convert-to-tex (ensure-list a)) (convert-to-tex (ensure-list b)))) #+END_SRC *** TODO Addition :PROPERTIES: :ID: 68f3dac3-9f0a-4fee-8da6-a39f4491f3ce :CREATED: <2016-04-30 Sat 16:19> :END: #+Caption: Rule for addition #+Name: tex-addition-rule #+BEGIN_SRC lisp (defrule addition (+ 2 >=) (&rest elements) (format nil "{~{{~a}~^ + ~}}" (map 'list #'convert-to-tex (map 'list #'ensure-list elements)))) #+END_SRC *** TODO Subtraction :PROPERTIES: :ID: 9a908130-af5e-4c87-bb07-13bd66c35fcf :CREATED: <2016-04-30 Sat 16:19> :END: #+Caption: Subtraction Rule #+Name: tex-subtraction-rule #+BEGIN_SRC lisp (defrule subtraction (- 2 >=) (&rest elements) (format nil "{~{{~a}~^ - ~}}" (map 'list #'convert-to-tex (map 'list #'ensure-list elements)))) #+END_SRC *** TODO Exponentials and Logarithmics :PROPERTIES: :CREATED: <2016-04-30 Sat 16:19> :ID: 269dc47f-5062-4081-a08e-d50188af6a57 :END: #+Caption: Exponentials and Logarithms #+Name: tex-exponentials-and-logarithms #+BEGIN_SRC lisp (defrule exp (exp 1 =) (expression) (format nil "{e^{~a}}" (convert-to-tex (ensure-list expression)))) (defrule expt (expt 2 =) (base exponent) (format nil "{~a ^ {~a}}" (convert-to-tex (ensure-list base)) (convert-to-tex (ensure-list exponent)))) (defrule natlog (log 1 =) (expression) (format nil "{\\ln {~a}}" (convert-to-tex (ensure-list expression)))) (defrule logarithm (log 2 =) (expression base) (format nil "{\\log_{~a}~a}" (convert-to-tex (ensure-list base)) (convert-to-tex (ensure-list expression)))) #+END_SRC *** TODO Trigonometrics :PROPERTIES: :CREATED: <2016-04-30 Sat 16:19> :ID: 837806c9-7174-43a3-80b2-355b645d46ed :END: #+Caption: Trigonometric Functions #+Name: tex-trigonometrics #+BEGIN_SRC lisp (defrule sin (sin 1 =) (arg) (format nil "{\\sin {~a}}" (convert-to-tex (ensure-list arg)))) (defrule cos (cos 1 =) (arg) (format nil "{\\cos {~a}}" (convert-to-tex (ensure-list arg)))) (defrule tan (tan 1 =) (arg) (format nil "{\\tan {~a}}" (convert-to-tex (ensure-list arg)))) (defrule csc (csc 1 =) (arg) (format nil "{\\csc {~a}}" (convert-to-tex (ensure-list arg)))) (defrule sec (sec 1 =) (arg) (format nil "{\\sec {~a}}" (convert-to-tex (ensure-list arg)))) (defrule cot (cot 1 =) (arg) (format nil "{\\cot {~a}}" (convert-to-tex (ensure-list arg)))) #+END_SRC *** TODO Logic :PROPERTIES: :CREATED: <2016-04-30 Sat 18:29> :ID: 74d12931-343f-4982-945d-738a3e38a1db :END: #+Caption: Logic Rules #+Name: tex-logic-rules #+BEGIN_SRC lisp (defrule and (and 2 >=) (&rest elements) (format nil "{~{{~a}~^ \\wedge ~}}" (map 'list #'convert-to-tex (map 'list #'ensure-list elements)))) (defrule or (or 2 >=) (&rest elements) (format nil "{~{{~a}~^ \\vee ~}}" (map 'list #'convert-to-tex (map 'list #'ensure-list elements)))) (defrule not (not 1 =) (&rest elements) (format nil "{\\not {~a}}" (map 'list #'convert-to-tex (map 'list #'ensure-list elements)))) #+END_SRC *** TODO Equality :PROPERTIES: :CREATED: <2016-04-30 Sat 18:29> :ID: f75273d2-d523-4404-925b-af6fd01c7520 :END: #+Caption: Equality Rules #+Name: tex-equality-rules #+BEGIN_SRC lisp (defrule = (= 2 =) (lhs rhs) (format nil "{{~a} = {~a}}" (convert-to-tex (ensure-list lhs)) (convert-to-tex (ensure-list rhs)))) #+END_SRC *** TODO Summation and Integration :PROPERTIES: :CREATED: <2016-04-30 Sat 18:30> :ID: dda2827a-cee5-4efc-bd9a-4dd953829b5c :END: #+Caption: Summation and Integration #+Name: tex-summation-and-integration #+BEGIN_SRC lisp (defrule sum (sum 3 =) (start stop expression) (format nil "{\\sum_{~a}^{~a} {~a}}" (convert-to-tex (ensure-list start)) (convert-to-tex (ensure-list stop)) (convert-to-tex (ensure-list expression)))) (defrule integrate (integrate 4 =) (from to expression wrt) (format nil "{\\int_{~a}^{~a} ~a\\,\mathrm{d}~a}" (convert-to-tex (ensure-list from)) (convert-to-tex (ensure-list to)) (convert-to-tex (ensure-list expression)) (convert-to-tex (ensure-list wrt)))) #+END_SRC *** TODO Specialty :PROPERTIES: :CREATED: <2016-04-30 Sat 18:30> :ID: f4e6b309-289d-4b32-bc55-4740ec86a113 :END: #+Caption: Specialty #+Name: tex-specialty #+BEGIN_SRC lisp (defrule parens (parens 2 =) (type inside) (let* ((types '((square . ("[" . "]")) (curly . ("{" . "}")) (smooth . ("(" . ")")))) (left (cadr (assoc type types))) (right (cddr (assoc type types)))) (format nil "{\\left~a {~a} \\right~a}" left (convert-to-tex (ensure-list inside)) right))) #+END_SRC ** TODO Conversion Driver :PROPERTIES: :ID: b395bdb7-7b98-49a1-b6d6-4256fb40d4fa :CREATED: <2016-04-30 Sat 16:19> :END: #+Caption: Conversion Driver #+Name: tex-conversion-driver #+BEGIN_SRC lisp (defvar *tex-outputp* nil) (declaim (special *tex-outputp*)) (defmacro with-tex-output (&body body) `(if *tex-outputp* (progn ,@body) (let ((*tex-outputp* t)) (format nil "$~a$" (progn ,@body))))) (defun convert-to-tex (function) (check-type function cons) (let ((op (first function))) (with-tex-output (cond ((numberp op) (format nil "~a" op)) ((and (symbolp op) (= 1 (length function))) (let ((symbol-pair (assoc op *special-symbols-to-sequences*))) (if (null symbol-pair) (string-downcase op) (cdr symbol-pair)))) (t (let ((expansion-function (get-expansion function))) (if (functionp expansion-function) (apply expansion-function (rest function)) (error "Undefined expansion for operation: ~a." op)))))))) #+END_SRC ** TODO Miscellaneous Functions :PROPERTIES: :CREATED: <2016-04-30 Sat 16:09> :ID: a4ab8a72-0b09-453c-b936-2470d5429c05 :END: #+Caption: Misc Functions #+Name: tex-misc-functions #+BEGIN_SRC lisp (defvar *special-symbols-to-sequences* '((alpha . "\\alpha") (beta . "\\beta") (gamma . "\\gamma") (delta . "\\delta") (epsilon . "\\epsilon") (varepsilon . "\\varepsilon") (zeta . "\\zeta") (eta . "\\eta") (theta . "\\theta") (vartheta . "\\vartheta") (gamma . "\\gamma") (kappa . "\\kappa") (lambda . "\\lambda") (mu . "\\mu") (nu . "\\nu") (xi . "\\xi") (omicron . "\\o") (pi . "\\pi") (varpi . "\\varpi") (rho . "\\rho") (varrho . "\\varrho") (sigma . "\\sigma") (varsigm . "\\varsigm") (tau . "\\tau") (upsilon . "\\upsilon") (phi . "\\phi") (varphi . "\\varphi") (chi . "\\chi") (psi . "\\psi") (omega . "\\omega") (big-gamma . "\\Gamma") (big-delta . "\\Delta") (big-theta . "\\Theta") (big-lambda . "\\Lambda") (big-xi . "\\Xi") (big-pi . "\\Pi") (big-sigma . "\\Sigma") (big-upsilon . "\\Upsilon") (big-phi . "\\Phi") (big-psi . "\\Psi") (big-omega . "\\Omega"))) #+END_SRC ** TODO Putting it Together :PROPERTIES: :ID: fdef3016-cb12-43ad-ba5f-14dd6ccd973c :CREATED: <2016-04-30 Sat 16:25> :END: #+Caption: Packaging #+Name: tex-packaging #+BEGIN_SRC lisp :tangle "to-tex.lisp" (in-package #:to-tex) <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> #+END_SRC * WORKING Library Assembly [0/2] :PROPERTIES: :CREATED: <2016-06-11 Sat 22:30> :END: ** TODO Package Definition :PROPERTIES: :CREATED: <2016-06-13 Mon 15:00> :ID: 573a8352-8cbe-408c-8c27-3cf0b66da885 :END: #+Caption: LARCS Packages #+Name: larcs-packages #+BEGIN_SRC lisp :tangle "larcs-packages.lisp" (defpackage #:manipulator (:use #:cl) (:import-from #:alexandria #:symbolicate) (:export #:manipulate #:classify #:classified-as-p #:classification-case #:collect-variables #:collect-terms)) (defpackage #:derive (:use #:cl) (:import-from #:alexandria #:symbolicate) (:import-from #:com.informatimago.common-lisp.cesarum.list #:aget #:ensure-list) (:export :derive :csc :sec :define-equation-functions :take-derivative)) (defpackage #:to-tex (:use #:cl) (:import-from #:alexandria #:symbolicate) (:import-from #:com.informatimago.common-lisp.cesarum.list #:aget #:ensure-list) (:export #:convert-to-tex)) #+END_SRC ** TODO System Definition :PROPERTIES: :CREATED: <2016-06-13 Mon 14:58> :ID: 35b2ec01-a933-4b5b-af73-b6b7f1c45cb6 :END: #+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 Flint " :license "GNU GPLv3 or Later" :depends-on (#:alexandria #:com.informatimago.common-lisp.cesarum.list) :serial t :components ((:file "larcs-packages") (:file "manipulation") (:file "derive"))) #+END_SRC * WORKING Text User Interface [0/2] :PROPERTIES: :CREATED: <2016-06-11 Sat 22:31> :END: ** TODO System Definition :PROPERTIES: :CREATED: <2016-06-13 Mon 14:51> :END: ** TODO Functionality :PROPERTIES: :CREATED: <2016-06-13 Mon 14:51> :END: * WORKING Graphical User Interface [0/3] :PROPERTIES: :CREATED: <2016-06-13 Mon 14:51> :END: ** TODO System Definition :PROPERTIES: :CREATED: <2016-06-13 Mon 14:51> :END: ** TODO Interface Elements :PROPERTIES: :CREATED: <2016-06-13 Mon 14:52> :END: ** TODO Interface Functionality :PROPERTIES: :CREATED: <2016-06-13 Mon 14:52> :END: