Thanks to visit codestin.com
Credit goes to github.com

Skip to content
This repository was archived by the owner on Jun 18, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 29 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,33 @@

## [unreleased]

- #448:

- new `g/infinite?` generic with implementations for all numeric types,
complex numbers, `differential` instances. Defaults to `false` for all other
types. (Also aliased into `sicmutils.env/infinite?`).

- The infix, TeX and JavaScript renderers (`->infix`, `->TeX` and
`->JavaScript`) all properly render `##Inf` and `##-Inf`. Infix uses the
Unicode symbol ∞, while `->TeX` uses the LaTeX command `\infty`.
Javascript's `Infinity` stands in for `##Inf` in generated JS code.

- Complex numbers now respond `true` to `g/negative?` if their imaginary
component is zero and real component is negative, false otherwise.

- `g/+`, `g/-`, `g//` now short circuit if there is a NUMERIC zero on either
side. This was causing bugs in cases where we allow, say, a scalar to be
added to a quaternion, and auto-convert the scalar right there (so it adds
only to the real part). OR in cases, like in the matrix PR, where we convert
the scalar in addition to `<scalar>*I*`.

- This caused some problems with `sicmutils.matrix` tests that were not well
typed.

- The default `expt` implementation is now available as a function to call
directly (`sicmutils.generic/default-expt`) without going through the
dispatch system.

- #447 contains a grab-bag of fixes and additions, many related to complex
numbers:

Expand Down Expand Up @@ -309,7 +336,8 @@ into shape.
> at our [Github Discussions](https://github.com/sicmutils/sicmutils/discussions)
> page!)

This release focused on improving the expressiveness and performance of the three simplification engines in SICMUtils:
This release focused on improving the expressiveness and performance of the
three simplification engines in SICMUtils:

- `sicmutils.polynomial` and `sicmutils.rational-function` are now quite well
fleshed out, with full polynomial and rational function APIs and many
Expand Down
8 changes: 7 additions & 1 deletion src/sicmutils/complex.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,13 @@
re
(complex re im))))

(defmethod g/negative? [::complex] [a] false)
(defmethod g/negative? [::complex] [a]
(and (v/zero? (imaginary a))
(g/negative? (real a))))

(defmethod g/infinite? [::complex] [a]
(or (g/infinite? (real a))
(g/infinite? (imaginary a))))

#?(:cljs
;; These are all defined explicitly in Complex.js.
Expand Down
5 changes: 4 additions & 1 deletion src/sicmutils/differential.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -1251,7 +1251,10 @@
(defbinary g/div (lift-2 g/div))

(defunary g/negative?
(fn [x] (g/negative? (finite-term x))))
(comp g/negative? finite-term))

(defunary g/infinite?
(comp g/infinite? finite-term))

(defunary g/abs
(fn [x]
Expand Down
4 changes: 2 additions & 2 deletions src/sicmutils/env.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
= core=}
:exclude [+ - * / zero? compare divide
numerator denominator
#?@(:cljs [= partial])])
#?@(:cljs [= partial infinite?])])
(:require #?(:clj [potemkin :refer [import-def import-vars]])
[sicmutils.abstract.function :as af #?@(:cljs [:include-macros true])]
[sicmutils.abstract.number :as an]
Expand Down Expand Up @@ -252,7 +252,7 @@ constant [Pi](https://en.wikipedia.org/wiki/Pi)."}
[sicmutils.generic
* + - / divide
negate
negative?
negative? infinite?
invert
abs
sqrt
Expand Down
74 changes: 47 additions & 27 deletions src/sicmutils/expression/render.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
[sicmutils.util :as u]
[sicmutils.value :as v]))

(defn ^:private make-symbol-generator [p]
(defn- make-symbol-generator [p]
(let [i (atom 0)]
(fn [] (symbol
#?(:clj
Expand Down Expand Up @@ -75,7 +75,7 @@
(v/integral? denom))
(str num "/" denom))))

(defn ^:private make-infix-renderer
(defn- make-infix-renderer
"Base function for infix renderers. This is meant to be specialized via
options for the treatment desired. Returns a rendering function. The options are:

Expand Down Expand Up @@ -316,12 +316,12 @@
"csc" "\\csc"
"_" "\\_"})))

(defn ^:private digit->int
(defn- digit->int
[^Character d]
#?(:clj (Character/digit d 10)
:cljs (js/parseInt d)))

(defn ^:private n->script
(defn- n->script
"Given an integer, returns a string where each digit of the
integer is used as the index into the replacement map scripts,
which is expected to be indexable by integers in the range [0..9]."
Expand All @@ -336,6 +336,15 @@
(merge non-TeX-greek
sym->unicode))

(defn- infinity->infix
"Given some infinite value, returns a string representation of ##Inf or ##-Inf
appropriate for infix rendering, else returns `nil`."
[x]
(case x
##Inf "∞"
##-Inf "-∞"
nil))

(def ^{:doc "Converts an S-expression to printable infix form. Numeric exponents
are written as superscripts. Partial derivatives get subscripts."}
->infix
Expand Down Expand Up @@ -371,34 +380,46 @@
'/ render-infix-ratio}
:render-primitive
(fn r [v]
(let [s (str v)]
(or (infix-sym->unicode s)
(condp re-find s
superscript-pattern
:>> (fn [[_ stem superscript]]
(if-let [n (re-matches #"[0-9]+" superscript)]
(str (r stem) (n->superscript n))
(str (r stem) "↑" (r superscript))))

subscript-pattern
:>> (fn [[_ stem subscript]]
(if-let [n (re-matches #"[0-9]+" subscript)]
(str (r stem) (n->subscript n))
(str (r stem) "_" (r subscript))))
v))))))

(defn ^:private brace
(or (infinity->infix v)
(let [s (str v)]
(or (infix-sym->unicode s)
(condp re-find s
superscript-pattern
:>> (fn [[_ stem superscript]]
(if-let [n (re-matches #"[0-9]+" superscript)]
(str (r stem) (n->superscript n))
(str (r stem) "↑" (r superscript))))

subscript-pattern
:>> (fn [[_ stem subscript]]
(if-let [n (re-matches #"[0-9]+" subscript)]
(str (r stem) (n->subscript n))
(str (r stem) "_" (r subscript))))
v)))))))

(defn- brace
"Wrap the argument, as a string, in braces"
[s]
(str "{" s "}"))

(defn ^:private maybe-brace
(defn- maybe-brace
"Wrap the argument in braces, as a string, unless it's just a single character"
[s]
(if (and (string? s) (= (count s) 1))
s
(brace s)))

(defn- infinity->tex
"Given some infinite value, returns a (string) representation of the LaTeX
commands required to render ##Inf or ##-Inf.

Returns `nil` for all other inputs."
[x]
(case x
##Inf "\\infty"
##-Inf "-\\infty"
nil))

(def ^{:dynamic true
:doc "If true, [[->TeX]] will render down tuples as vertical matrices
with square braces. Defaults to false."}
Expand Down Expand Up @@ -515,10 +536,9 @@
'>= #(s/join " \\geq " %)}
:render-primitive
(fn r [v]
(cond (r/ratio? v)
(str "\\frac" (brace (r/numerator v)) (brace (r/denominator v)))

:else
(if (r/ratio? v)
(str "\\frac" (brace (r/numerator v)) (brace (r/denominator v)))
(or (infinity->tex v)
(let [s (str v)]
(or (TeX-map s)
(condp re-find s
Expand Down Expand Up @@ -549,7 +569,7 @@
(if *TeX-sans-serif-symbols*
(str "\\mathsf" (brace s))
(brace s))
v)))))))))
v))))))))))

(defn ->TeX
"Convert the given expression to TeX format, as a string.
Expand Down
45 changes: 32 additions & 13 deletions src/sicmutils/generic.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
cljdocs](https://cljdoc.org/d/sicmutils/sicmutils/CURRENT/doc/basics/generics)
for a detailed discussion of how to use and extend the generic operations
defined in [[sicmutils.generic]] and [[sicmutils.value]]."
(:refer-clojure :exclude [/ + - * divide])
(:refer-clojure :exclude [/ + - * divide #?@(:cljs [infinite?])])
(:require [sicmutils.value :as v]
[sicmutils.util :as u]
[sicmutils.util.def :refer [defgeneric]
Expand Down Expand Up @@ -88,8 +88,8 @@
([] 0)
([x] x)
([x y]
(cond (v/zero? x) y
(v/zero? y) x
(cond (v/numeric-zero? x) y
(v/numeric-zero? y) x
:else (add x y)))
([x y & more]
(reduce + (+ x y) more)))
Expand Down Expand Up @@ -137,8 +137,8 @@
([] 0)
([x] (negate x))
([x y]
(cond (v/zero? y) x
(v/zero? x) (negate y)
(cond (v/numeric-zero? y) x
(v/numeric-zero? x) (negate y)
:else (sub x y)))
([x y & more]
(- x (apply + y more))))
Expand Down Expand Up @@ -244,7 +244,7 @@
([] 1)
([x] (invert x))
([x y]
(if (v/one? y)
(if (and (v/number? y) (v/one? y))
x
(div x y)))
([x y & more]
Expand Down Expand Up @@ -276,7 +276,16 @@
0)
(mul (log x) (expt x y))))})

(defmethod expt :default [s e]
(defn ^:no-doc default-expt
"Default implementation of exponentiation for integral exponents `e`.

This implementation uses ['Exponentation by
Squaring'](https://en.wikipedia.org/wiki/Exponentiation_by_squaring), and will
work for any type that implements `g/mul`.

The multiplication operation is looked up once and cached at the beginning of
computation."
[s e]
{:pre [(v/native-integral? e)]}
(let [kind (v/kind s)]
(if-let [mul' (get-method mul [kind kind])]
Expand All @@ -295,6 +304,8 @@
:else (invert (expt' s (negate e)))))
(u/illegal (str "No g/mul implementation registered for kind " kind)))))

(defmethod expt :default [s e] (default-expt s e))

(defgeneric square 1)
(defmethod square :default [x] (expt x 2))

Expand Down Expand Up @@ -347,6 +358,13 @@
(defmethod negative? :default [a]
(< a (v/zero-like a)))

(defgeneric infinite? 1
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

still pending.

"Returns true if `a` is either numerically infinite (ie, equal to `##Inf`) or
a compound number (complex or quaterion, for example) with some infinite
component.")

(defmethod infinite? :default [a] false)

(defgeneric abs 1)

(declare integer-part)
Expand Down Expand Up @@ -579,7 +597,12 @@
(defmethod determinant [::v/scalar] [a] a)
(defmethod dimension [::v/scalar] [a] 1)
(defmethod dot-product [::v/scalar ::v/scalar] [l r] (mul l r))
(defmethod inner-product [::v/scalar ::v/scalar] [l r] (mul (conjugate l) r))

;; Scalars include complex numbers, but for the purposes of dot/inner-products
;; these are interpreted as pairs of real numbers, where conjugate is identity.
;; So this seems to be a sane default.
(defmethod inner-product [::v/scalar ::v/scalar] [l r]
(dot-product l r))

;; ## Solvers

Expand Down Expand Up @@ -621,11 +644,6 @@
(defgeneric simplify 1)
(defmethod simplify :default [a] a)

(defn factorial
"Returns the factorial of `n`, ie, the product of 1 to `n` (inclusive)."
[n]
(apply * (range 1 (inc n))))

;; This call registers a symbol for any non-multimethod we care about. These
;; will be returned instead of the actual function body when the user
;; calls `(v/freeze fn)`, for example.
Expand All @@ -643,6 +661,7 @@
clojure.core/quot 'quotient
clojure.core/rem 'remainder
clojure.core/neg? 'negative?
#?@(:cljs [cljs.core/infinite? 'infinite?])
clojure.core/< '<
clojure.core/<= '<=
clojure.core/> '>
Expand Down
6 changes: 6 additions & 0 deletions src/sicmutils/numbers.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@
#?(:clj (long a)
:cljs (Math/trunc a)))

(defmethod g/infinite? [::v/integral] [a] false)
(defmethod g/infinite? [::v/real] [a]
#?(:clj (or (= a ##Inf)
(= a ##-Inf))
:cljs (infinite? a)))

;; ## Complex Operations
(defmethod g/real-part [::v/real] [a] a)
(defmethod g/imag-part [::v/real] [a] 0)
Expand Down
3 changes: 3 additions & 0 deletions src/sicmutils/ratio.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,8 @@
(g/lcm (core-denominator a)
(core-denominator b))))

(defmethod g/infinite? [Ratio] [a] false)

(doseq [[op f] [[g/exact-divide /]
[g/quotient quot]
[g/remainder rem]
Expand Down Expand Up @@ -269,6 +271,7 @@

(defmethod g/negate [Fraction] [a] (promote (.neg ^js a)))
(defmethod g/negative? [Fraction] [a] (neg? (obj/get a "s")))
(defmethod g/infinite? [Fraction] [a] false)
(defmethod g/invert [Fraction] [a] (promote (.inverse ^js a)))
(defmethod g/square [Fraction] [a] (promote (.mul ^js a a)))
(defmethod g/cube [Fraction] [a] (promote (.pow ^js a 3)))
Expand Down
Loading