(ns emmy.abstract.number
"Symbolic expressions in Emmy are created through the [[literal-number]]
constructor, or implicitly by performing arithmetic between symbols and
numbers.
This namespace implements the [[literal-number]] constructor and installs the
underlying type into the generic arithmetic system."
(:require [emmy.expression :as x]
[emmy.generic :as g]
[emmy.numsymb :as sym]
[emmy.simplify :as ss]
[emmy.value :as v])
#?(:clj
(:import (clojure.lang Symbol))))
(extend-type Symbol
v/Numerical
(numerical? [_] true)
v/IKind
(kind [_] Symbol))
nil
(defn literal-number
"Returns its argument, wrapped in a marker type that responds to the generic
operations registered in [[emmy.numsymb]].
Symbols are automatically treated as [[literal-number]] instances, so
```clojure
(* 10 (literal-number 'x))
```
is equivalent to
```clojure
(* 10 'x)
```
If you pass an actual number, emmy will attempt to preserve exact values
through various operations:
```clojure
(g/+ 1 (g/cos (g/* 2 (literal-number 4))))
;;=> (+ 1 (cos 8))
```
Notice that the `(g/* 2 ...)` is evaluated, but `cos` evaluation is deferred,
since the result is inexact. On the other hand, if the number is inexact to
begin with:
```clojure
(g/+ 1 (g/cos (g/* 2 (literal-number 2.2))))
;;=> 0.6926671300215806
```
the system will go ahead and evaluate it."
[x]
(x/make-literal ::x/numeric x))
#object[emmy.abstract.number$literal_number 0x1da16095 "
emmy.abstract.number$literal_number@1da16095"
]
(defn literal-number?
"Returns true if `x` is an explicit symbolic expression or something passed to
`literal-number`, false otherwise.
See [[abstract-number?]] for a similar function that also responds true to
symbols."
[x]
(and (x/literal? x)
(= (x/literal-type x) ::x/numeric)))
#object[emmy.abstract.number$literal_number_QMARK_ 0x21e8faf6 "
emmy.abstract.number$literal_number_QMARK_@21e8faf6"
]
(defn abstract-number?
"Returns true if `x` is:
- a symbolic expression
- some object wrapped by a call to [[literal-number]]
- a symbol (which implicitly acts as a [[literal-number]])
See [[literal-number?]] for a similar function that won't respond true to
symbols, only to explicit symbolic expressions or wrapped literal numbers."
[x]
(or (literal-number? x)
(symbol? x)))
#object[emmy.abstract.number$abstract_number_QMARK_ 0x2007f425 "
emmy.abstract.number$abstract_number_QMARK_@2007f425"
]

Generic Installation

(derive Symbol ::x/numeric)
nil
(derive ::x/numeric ::v/scalar)
nil

This installs equality into v/= between symbolic expressions (and symbols, see inheritance above), sequences where appropriate, and anything in the standard numeric tower.

(defmethod v/= [Symbol v/seqtype] [_ _] false)
#object[clojure.lang.MultiFn 0x744f207b "
clojure.lang.MultiFn@744f207b"
]
(defmethod v/= [v/seqtype Symbol] [_ _] false)
#object[clojure.lang.MultiFn 0x744f207b "
clojure.lang.MultiFn@744f207b"
]
(defmethod v/= [Symbol ::v/number] [_ _] false)
#object[clojure.lang.MultiFn 0x744f207b "
clojure.lang.MultiFn@744f207b"
]
(defmethod v/= [::v/number Symbol] [_ _] false)
#object[clojure.lang.MultiFn 0x744f207b "
clojure.lang.MultiFn@744f207b"
]
(defmethod v/= [::x/numeric v/seqtype] [l r] (v/= (x/expression-of l) r))
#object[clojure.lang.MultiFn 0x744f207b "
clojure.lang.MultiFn@744f207b"
]
(defmethod v/= [v/seqtype ::x/numeric] [l r] (v/= l (x/expression-of r)))
#object[clojure.lang.MultiFn 0x744f207b "
clojure.lang.MultiFn@744f207b"
]
(defmethod v/= [::x/numeric ::v/number] [l r] (v/= (x/expression-of l) r))
#object[clojure.lang.MultiFn 0x744f207b "
clojure.lang.MultiFn@744f207b"
]
(defmethod v/= [::v/number ::x/numeric] [l r] (v/= l (x/expression-of r)))
#object[clojure.lang.MultiFn 0x744f207b "
clojure.lang.MultiFn@744f207b"
]
(defmethod v/= [::x/numeric ::x/numeric] [l r]
(= (x/expression-of l)
(x/expression-of r)))
#object[clojure.lang.MultiFn 0x744f207b "
clojure.lang.MultiFn@744f207b"
]
(defn- defunary [generic-op op-sym]
(if-let [op (sym/symbolic-operator op-sym)]
(defmethod generic-op [::x/numeric] [a]
(let [newexp (op (x/expression-of a))]
(literal-number
(if-let [simplify sym/*incremental-simplifier*]
(simplify newexp)
newexp))))
(defmethod generic-op [::x/numeric] [a]
(x/literal-apply ::x/numeric op-sym [a]))))
#object[emmy.abstract.number$defunary 0x62fc53af "
emmy.abstract.number$defunary@62fc53af"
]
(defn- defbinary [generic-op op-sym]
(let [pairs [[::x/numeric ::x/numeric]
[::v/number ::x/numeric]
[::x/numeric ::v/number]]]
(if-let [op (sym/symbolic-operator op-sym)]
(doseq [[l r] pairs]
(defmethod generic-op [l r] [a b]
(let [newexp (op (x/expression-of a)
(x/expression-of b))]
(literal-number
(if-let [simplify sym/*incremental-simplifier*]
(simplify newexp)
newexp)))))
(doseq [[l r] pairs]
(defmethod generic-op [l r] [a b]
(x/literal-apply ::x/numeric op-sym [a b]))))))
#object[emmy.abstract.number$defbinary 0x4253a4c "
emmy.abstract.number$defbinary@4253a4c"
]
(defbinary g/add '+)
nil
(defbinary g/sub '-)
nil
(defbinary g/mul '*)
nil
(defbinary g/div '/)
nil
(defbinary g/modulo 'modulo)
nil
(defbinary g/remainder 'remainder)
nil
(defbinary g/expt 'expt)
nil
(defunary g/negate 'negate)
#object[clojure.lang.MultiFn 0x1b9c4031 "
clojure.lang.MultiFn@1b9c4031"
]
(defunary g/invert 'invert)
#object[clojure.lang.MultiFn 0x165df7c1 "
clojure.lang.MultiFn@165df7c1"
]
(defunary g/integer-part 'integer-part)
#object[clojure.lang.MultiFn 0x12b60d1 "
clojure.lang.MultiFn@12b60d1"
]
(defunary g/fractional-part 'fractional-part)
#object[clojure.lang.MultiFn 0x4430be99 "
clojure.lang.MultiFn@4430be99"
]
(defunary g/floor 'floor)
#object[clojure.lang.MultiFn 0x6b6c4b9d "
clojure.lang.MultiFn@6b6c4b9d"
]
(defunary g/ceiling 'ceiling)
#object[clojure.lang.MultiFn 0x775a6a7c "
clojure.lang.MultiFn@775a6a7c"
]
(defunary g/sin 'sin)
#object[clojure.lang.MultiFn 0x163caf8e "
clojure.lang.MultiFn@163caf8e"
]
(defunary g/cos 'cos)
#object[clojure.lang.MultiFn 0x3be6105f "
clojure.lang.MultiFn@3be6105f"
]
(defunary g/tan 'tan)
#object[clojure.lang.MultiFn 0x428252da "
clojure.lang.MultiFn@428252da"
]
(defunary g/sec 'sec)
#object[clojure.lang.MultiFn 0x3b1846b4 "
clojure.lang.MultiFn@3b1846b4"
]
(defunary g/csc 'csc)
#object[clojure.lang.MultiFn 0x482bde86 "
clojure.lang.MultiFn@482bde86"
]
(defunary g/asin 'asin)
#object[clojure.lang.MultiFn 0x43c45b34 "
clojure.lang.MultiFn@43c45b34"
]
(defunary g/acos 'acos)
#object[clojure.lang.MultiFn 0x1380d514 "
clojure.lang.MultiFn@1380d514"
]
(defunary g/atan 'atan)
#object[clojure.lang.MultiFn 0x6a540c36 "
clojure.lang.MultiFn@6a540c36"
]
(defbinary g/atan 'atan)
nil
(defunary g/acot 'acot)
#object[clojure.lang.MultiFn 0x7c9d3014 "
clojure.lang.MultiFn@7c9d3014"
]
(defunary g/sinh 'sinh)
#object[clojure.lang.MultiFn 0x1d61d948 "
clojure.lang.MultiFn@1d61d948"
]
(defunary g/cosh 'cosh)
#object[clojure.lang.MultiFn 0x8dc9785 "
clojure.lang.MultiFn@8dc9785"
]
(defunary g/tanh 'tanh)
#object[clojure.lang.MultiFn 0x42231f14 "
clojure.lang.MultiFn@42231f14"
]
(defunary g/coth 'coth)
#object[clojure.lang.MultiFn 0x57ab8ad4 "
clojure.lang.MultiFn@57ab8ad4"
]
(defunary g/sech 'sech)
#object[clojure.lang.MultiFn 0x4493b2f0 "
clojure.lang.MultiFn@4493b2f0"
]
(defunary g/csch 'csch)
#object[clojure.lang.MultiFn 0x281f7558 "
clojure.lang.MultiFn@281f7558"
]
(defunary g/abs 'abs)
#object[clojure.lang.MultiFn 0x721e14f0 "
clojure.lang.MultiFn@721e14f0"
]
(defunary g/sqrt 'sqrt)
#object[clojure.lang.MultiFn 0x79e8dd4d "
clojure.lang.MultiFn@79e8dd4d"
]
(defunary g/log 'log)
#object[clojure.lang.MultiFn 0x20b5f4b2 "
clojure.lang.MultiFn@20b5f4b2"
]
(let [log (sym/symbolic-operator 'log)
div (sym/symbolic-operator '/)]
(defmethod g/log2 [::x/numeric] [a]
(let [a (x/expression-of a)]
(literal-number
(div (log a)
(log 2)))))
(defmethod g/log10 [::x/numeric] [a]
(let [a (x/expression-of a)]
(literal-number
(div (log a) (log 10))))))
#object[clojure.lang.MultiFn 0x73d3e049 "
clojure.lang.MultiFn@73d3e049"
]
(defunary g/exp 'exp)
#object[clojure.lang.MultiFn 0x7168c357 "
clojure.lang.MultiFn@7168c357"
]
(defbinary g/make-rectangular 'make-rectangular)
nil
(defbinary g/make-polar 'make-polar)
nil
(defunary g/real-part 'real-part)
#object[clojure.lang.MultiFn 0x4ed57a82 "
clojure.lang.MultiFn@4ed57a82"
]
(defunary g/imag-part 'imag-part)
#object[clojure.lang.MultiFn 0x41ca178e "
clojure.lang.MultiFn@41ca178e"
]
(defunary g/magnitude 'magnitude)
#object[clojure.lang.MultiFn 0x2df77656 "
clojure.lang.MultiFn@2df77656"
]
(defunary g/angle 'angle)
#object[clojure.lang.MultiFn 0x1ab27ac4 "
clojure.lang.MultiFn@1ab27ac4"
]
(defunary g/conjugate 'conjugate)
#object[clojure.lang.MultiFn 0x7fd6127a "
clojure.lang.MultiFn@7fd6127a"
]
(defbinary g/dot-product 'dot-product)
nil
(defbinary g/inner-product 'inner-product)
nil
(defbinary g/gcd 'gcd)
nil
(defbinary g/lcm 'lcm)
nil

We currently default to false here; once literals gain metadata saying whether or not they are negative, we return /something/. Maybe this is ill-founded, but it was required for some polynomial code.

(defmethod g/negative? [::x/numeric] [_] false)
#object[clojure.lang.MultiFn 0x6e310c69 "
clojure.lang.MultiFn@6e310c69"
]
(defmethod g/zero? [Symbol] [_] false)
#object[clojure.lang.MultiFn 0x78e2923f "
clojure.lang.MultiFn@78e2923f"
]
(defmethod g/one? [Symbol] [_] false)
#object[clojure.lang.MultiFn 0x710fa3bd "
clojure.lang.MultiFn@710fa3bd"
]
(defmethod g/identity? [Symbol] [_] false)
#object[clojure.lang.MultiFn 0x3e853a24 "
clojure.lang.MultiFn@3e853a24"
]
(defmethod g/freeze [Symbol] [s] s)
#object[clojure.lang.MultiFn 0x7a0db79a "
clojure.lang.MultiFn@7a0db79a"
]
(defmethod g/exact? [Symbol] [_] false)
#object[clojure.lang.MultiFn 0x1864e04e "
clojure.lang.MultiFn@1864e04e"
]
(defmethod g/simplify [Symbol] [a] a)
#object[clojure.lang.MultiFn 0x41843e3e "
clojure.lang.MultiFn@41843e3e"
]
(defmethod g/simplify [::x/numeric] [a]
(literal-number
(ss/simplify-expression
(x/expression-of a))))
#object[clojure.lang.MultiFn 0x41843e3e "
clojure.lang.MultiFn@41843e3e"
]
(def ^:private memoized-simplify
(memoize g/simplify))
#object[clojure.core$memoize$fn__6946 0x58b12bd9 "
clojure.core$memoize$fn__6946@58b12bd9"
]
(defn ^:no-doc simplify-numerical-expression
"This function will only simplify instances of [[expression/Literal]]; if `x` is
of that type, [[simplify-numerical-expression]] acts as a memoized version
of [[generic/simplify]]. Else, acts as identity.
This trick is used in [[emmy.calculus.manifold]] to memoize
simplification _only_ for non-[[differential/Differential]] types."
[x]
(if (literal-number? x)
(memoized-simplify x)
x))
#object[emmy.abstract.number$simplify_numerical_expression 0x34ae399a "
emmy.abstract.number$simplify_numerical_expression@34ae399a"
]