Skip to content

Instantly share code, notes, and snippets.

@sritchie
Created December 12, 2020 20:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sritchie/20073eece3ef227f755949251709192b to your computer and use it in GitHub Desktop.
Save sritchie/20073eece3ef227f755949251709192b to your computer and use it in GitHub Desktop.
v0.14.0: tons of new generic operations, refman ported!
  • we have a great documentation site now! cljdoc now hosts a full port of the original scmutils reference manual. All code snippets in the ported refman now work. I've also distributed many of the sections in the reference manual into more topically organized sections in the cljdoc site, so give those a browse and keep your eye out for more expansion there.

Okay, on to the beefy changelog.

A quick note: After the work below, v/nullity? renamed to v/zero?, and v/unity? renamed to v/one? #180. This affects the names listed in the CHANGELOG entries below.

New Generic Functions

This release brings us closer to the interface provided by scmutils.

PR #193 brings:

  • g/dot-product, for scalars, differentials, structures, functions and row/column matrices
  • g/inner-product for scalars, structures, functions and row/column matrices
  • g/outer-product for functions, structures of length 3 and matrices, between a row and a column only
  • g/cross-product now works for row/column matrices in addition to structures (and functions that accept these)

PR sicmutils/sicmutils#169 brings:

  • g/exp2, g/exp10 for exponents with base 2 and 10
  • g/log2, for base 2 logarithms
  • g/log10 for base 10 logs
  • g/gcd and g/lcm are now exposed in sicmutils.env

#178 introduces:

  • g/dimension for scalars (always 1), structures and matrices (square, column and row)
  • g/trace returns the trace for square matrices and square structures

We now expose the following additional trigonometric functions in sicmutils.generic (courtesy of sicmutils/sicmutils#154):

  • cosh: hyperbolic cosine
  • sinh: hyperbolic sine
  • tanh: hyperbolic tangent, ie sinh/cosh
  • sech: hyperbolic secant, ie 1/cosh
  • csch: hyperbolic secant, ie 1/sinh
  • acosh: inverse hyperbolic cosine, ie, (= x (cosh (acosh x)))
  • asinh: inverse hyperbolic sine, ie, (= x (sinh (asinh x)))
  • atanh: inverse hyperbolic tangent, ie, (= x (tanh (atanh x)))

These three methods existed in sicmutils.env, but not as extensible generics. Now they're fully extensible:

  • cot: cotangent, ie 1/tan
  • sec: secant, ie 1/cos
  • csc: cosecant, ie 1/sin

These all work with:

  • real and complex numbers
  • power series (missing a few implementations, operators and matrices are missing the same ones for this reason)
  • matrices (square matrices return their power series expansions)
  • operators (power series expansion of the operator)
  • functions (where they create composition)
  • symbolic expressions
  • Derivatives and dual numbers! The new functions all work with D, the forward-mode automatic differentiation operator.

Additionally, four methods that lived in sicmutils.generic are now exposed as generics:

  • real-part
  • imag-part
  • angle
  • conjugate

These now work on:

  • all numeric types, including symbolic expressions.

  • functions

  • structures (only magnitude and conjugate)

    • magnitude formerly didn't handle structures containing complex numbers by taking a proper inner product. This is fixed as of #168
  • PR #189 introduces:

    • g/make-rectangular, (build a complex number from real and imaginary parts)
    • g/make-polar (build a complex number from radius and angle)
    • note that these work with real numbers, symbolic numbers, functions and any combination of these.

These work with functions, real numbers and symbolic expressions (and any mix of the three).

Literals

Numeric Tower Adjustments

This release (courtesy of #168) brings the numeric tower in line with the scmutils tower. Prior to this release, all numbers, including complex, descended from ::x/numerical-expression. Symbolic expressions also derived from this type! The problem this causes is that all of generic implementations for the number types default to the symbolic functions.

If I don't specify a g/sec method for numbers, for example, then (g/sec 12) returns a symbolic (/ 1 (cos 12)), instead of actually evaluating the expression.

The fix comes from these changes:

  • ::v/number now means, "the numeric tower ascending from integer -> rational -> real -> complex numbers. All of these types now respond true to v/number? (prior to this release, Complex numbers did NOT!)

  • ::v/real now means, "anything in the numeric tower except Complex". These all respond true to v/real?

  • ::x/numeric-expression has changed to ::x/numeric, and now means "anything that responds to ::"v/number, plus symbolic expressions, which now clearly represent any number in the numeric tower. Query for these with v/scalar?

I can now make some comments that clear up my former misunderstandings:

  • The sicmutils.abstract.number (I'll call this an here) namespace is responsible for installing generic implementations of all numeric methods for symbolic expressions and "literal numbers".

  • the an/literal-number constructor promotes a number, symbol or symbolic expression up to :xx/numeric, which means that any operation you perform on it will pass it through the symbolic expressions defined in sicmutils.numsymb. A few notes on these expressions:

    • They will try to preserve exactness, but if they can't - ie, if you do something like (cos (an/literal-number 2.2)) - the system will return -.588. If you call (cos (an/literal-number 2)), you'll get the expression (cos 2), preserving exactness.

    • Symbols are automatically interpreted as "literal numbers".

    • The only ways to make a proper symbolic expression that works with the generics are:

      • Use the explicit an/literal-number constructor
      • pass a symbol to any generic arithmetic function
      • perform any unary or binary arithmetic operation on an existing symbolic expression.
    • an/abstract-number? returns true for symbolic expressions, anything wrapped in literal-number or symbols.

    • literal-number? only returns true for explicitly wrapped things and symbolic expressions, not symbols.

    • use v/real?, v/number? and v/scalar? to query the numeric tower.

  • If you want to compare literal numbers and an expression like (an/literal-number 12), use v/=. In Clojurescript, this will work with the built in = as well, since equality is implemented with a protocol that we can extend. For example:

(v/= 12 (literal-number 12))
;;=> true

(= 12 (literal-number 12))
;; true in cljs, false in clj

If you keep the literal on the left side of =, this will work in both systems, since we've overridden the = implementation for literals:

(= (literal-number 12) 12)
;;=> true in both languages

This paves the way for the other abstract types that exist in scmutils, like matrices, up and down tuples.

Miscellaneous

  • expose bootstrap-repl! to Clojurescript, so that this is available in self-hosted CLJS (sicmutils/sicmutils#157)

  • modified infix.cljc to wrap forms in displaystyle and add proper carriage returns inside structures (sicmutils/sicmutils#157)

  • add multidimensional-minimize to the sicmutils.env namespace (sicmutils/sicmutils#157)

  • add more sqrt simplification rules to allow square roots to cancel out across a division boundary, with or without products in the numerator and denominator (sicmutils/sicmutils#160)

  • fix NPE bug that appears in nelder-mead, when callback isn't supplied (sicmutils/sicmutils#162)

  • Add sqrt-expand and sqrt-contract, to allow simplifications to push inside of square roots (sicmutils/sicmutils#163)

  • speed up power series multiplication by skipping work when either head term is zero (sicmutils/sicmutils#166)

  • File moves:

    • sicmutils.polynomial-gcd => sicmutils.polynomial.gcd
    • sicmutils.polynomial-factor => sicmutils.polynomial.factor
    • sicmutils.rules => sicmutils.simplify.rules
    • sicmutils.analyze => sicmutils.expression.analyze
    • sicmutils.infix => sicmutils.expression.render
    • sicmutils.numerical.compile => sicmutils.expression.compile
  • sicmutils.env/one? now exposes/aliases sicmutils.value/unity? #154

  • Fixed #93 by adding an explicit g/invert implementation for polynomials in the rational fn namespace. The fix lives in #169.

  • added sicmutils.value/sqrt-machine-epsilon (#170)

  • fixed issues in function.cljc and operator.cljc where the Clojurescript IFn -invoke arguments shadowed either the this operator, or some parameter name in the deftype (#169)

  • g/sqrt now maintains precision with Clojurescript's rational numbers. (g/sqrt #sicm/ratio 9/4) for example returns #sicm/ratio 3/2. (#168)

  • g/determinant and g/transpose now act as identity for everything in the numeric tower, plus symbolic expressions (#168)

  • sicmutils.expression.Expression is now sicmutils.expression.Literal; it has a new meta field, and is a deftype instead of a defrecord. (#168)

    • To get the internal expression, use x/expression-of instead of :expression.
    • to access the type field, use x/literal-type instead of :type
  • 2-arity g/atan, g/cross-product and g/gcd now work for functions (#168)

  • Literal now responds appropriately to v/unity? and v/nullity? if it wraps a numerical "0" or "1". v/exact? now returns true if the literal wraps an exact number (#168)

  • x/variables-in now works with wrapped expressions; no more need to explicitly unwrap (#168)

  • x/walk-expression renamed x/evaluate (#168)

  • The new x/substitute performs substitutions on an unwrapped expression (#168)

  • x/compare returns a comparator that works with unwrapped symbolic expression trees (#168). The rules are that that types have the following ordering:

  • empty sequence is < anything (except another empty seq)

  • real < symbol < string < sequence

  • sequences compare element-by-element

  • Any types NOT in this list compare using hashes

  • g/transpose now works properly for functions that act as linear maps. The defining relation is:

(= (((transpose f) g) 'x)
   (g (f x)))
  • added g/determinant implementation to functions (#171)

  • Moved all literal-function machinery and definitions to sicmutils.abstract.function (#171). sicmutils.function now contains only the generic method implementations for clojure functions and multimethods.

  • Switched inheritance order for functions; :sicmutils.abstract.function/function (used to be :sicmutils.function/function) now inherits from ::v/function instead of the other way around. (#171)

  • Enhanced the g/simplify behavior for core functions that overlap with generic functions (+, -, *, /, mod, quot, rem, neg?). These now freeze to the same symbols as their generic counterparts. (#173)

  • Add support for the hyperbolic trig functions sinh, cosh, tanh, atanh, asinh and acosh to sicmutils.expression.render/->Javascript. (#174)

  • Add support for the hyperbolic trig functions atanh, asinh and acosh to sicmutils.expression.compile. (#175)

  • matrix.cljc gains m/nth-col and m/diagonal (#178 introduces:)

  • As of #178 introduces:, we have three new kinds for matrices. Square matrices return ::m/square-matrix, and columns and rows return ::m/column-matrix and ::row-matrix respectively. These all derive from ::m/matrix. This makes it easier to register methods or test specifically for these cases. We've also added m/column? and m/row? predicates to check for these cases.

  • #185 specializes all matrix operations that return power series (trig operations and g/exp to ::square-matrix).

  • #184 modifies v/exact? on functions; ((v/exact? f) x) == (v/exact? (f x)) now, instead of false as before. literal-function forms now have a correct v/one-like implementation.

  • clojure Vars now respond to function algebra (#184). All functions implement g/negative?, g/abs, g/quotient, g/remainder, g/modulo, g/dimension and g/exact-divide, responding to the appropriate arities.

  • sicmutils.complex/complex can now take any real type in its constructor, vs only numbers (#184).

  • modint instances now implement v/freeze?: (sicmutils.modint/make 1 2) freezes to that (modint 1 2). (#185).

  • v/eq renamed to v/=. (#186).

  • v/zero-like on matrices now fills entries with appropriate v/zero-like versions of their existing types (#188)

  • v/Value gains identity-like and identity (#188). These are aliased into sicmutils.env. Implementations are installed on:

    • all numeric types, symbolic expressions, Differential (they return 1 of the appropriate type)
    • native and abstract functions, vars (they return an identity function)
    • operators (return an identity operator, same as one-like)
    • matrices (identity matrix, only works with ::m/square-matrix)
    • Polynomial (only works on monomials for now, returns an identity polynomial)
    • RationalFunction (returns the identity poly divided by unit poly, so only works on monomials by extension)
    • ModInt (returns the same as one-like)
    • Series and PowerSeries (returns [0 1 0 0 0 0...]). This is slightly suspect in the case of Series, since Series, unlike PowerSeries, are general infinite sequences and not necessarily interpreted as polynomials. This decision follows scmutils convention.
  • sicmutils.complex/I aliases i (#189)

  • matrix.cljc has a new by-cols (analogous to m/by-rows), and row to generate a row matrix (analagous to column). #197 Also in matrix.cljc:

    • num-rows, num-cols access the row or column number without inspecting the deftype variables directly
    • fmap-indexed, like fmap but receives i and j indices as second and third arguments. -
    • with-substituted-row, for swapping out a single row in a matrix
    • submatrix generates a submatrix from low and high row and cols
    • matrix-some renamed to some: make sure to use a namespace prefix to avoid clashing with clojure.core/some.
    • new-matrix constructor by-cols (analogous to by-rows, takes a sequence of columns)
    • row constructor takes a sequence of values and returns a row matrix.
    • by-rows*, by-cols*, row* and column* are non-variadic versions of those functions. If you already have a sequence of rows, columns or elements, prefer these.
    • up->row-matrix => down->row-matrix and row-matrix->up => row-matrix->down. A row is analogous to a down, so we make a change to reflect this.
    • g/cross-product between two down structures now returns a down.
    • make-zero generates a zero-valued matrix of the supplied dimensions.
    • make-diagonal generates a diagonal matrix containing the values of the supplied sequence.
    • m/identity-like returns an identity matrix (given a square matrix) with entries of identical type, but set appropriately to zero or one. This is installed as v/one-like and v/identity-like.
    • v/identity? now returns true for identity matrices, false otherwise. v/one? returns false for identity matrices! If it didn't, (* 2 (I 10)) would return 2, since one? signals multiplicative identity.
  • sicmutils.structure/up and sicmutils.structure/down now have analogous s/up* and s/down* functions. These behave identically, but are non-variadic. If you already have a sequence you'd like to transform, prefer these (#197).

  • sicmutils.value/kind-predicate takes some item and returns a predicate that returns true if its argument has the same type (or inherits from it) (#197).

  • sicmutils.function/arg-shift and sicmutils.function/arg-scale take functions and return new functions that shift and scale their arguments (respectively) by the originally supplied shifts (#197).

  • sicmutils.generic/factorial computes the factorial of the supplied integer n. (#197).

  • Many new functions and constants exposed in sicmutils.env via #197:

    • -pi joins pi as a constant
    • s:generate, m:generate, vector:generate to generate matrices, structures and vectors
    • constant-series, from series/constant
    • seq:print and seq:pprint
    • matrix-by-cols, row-matrix, v:make-basis-unit
    • aliases for sicmutils.function's arity, arg-shift, arg-scale
    • dimension, factorial aliased from sicmutils.generic
    • derivative aliased from sicmutils.calculus.derivative
    • submatrix, up->column-matrix, down->row-matrix, row-matrix->{down,vector}, column-matrix->{up,vector} aliased from sicmutils.matrix
    • D-numeric from sicmutils.numerical.derivative
    • brent-min, brent-max, golden-section-min, golden-section-max
    • nelder-mead
    • sum from `sicmutils.util.aggregate
    • kind-predicate from sicmutils.value
  • Structures and matrices both gain the ability to do native get-in, assoc-in and empty. These work as expected, like a potentially nested vector. (#193)

  • matrix.cljc gains up->row-matrix, up->column-matrix, row-matrix->up, column-matrix->up (#193)

  • structure.cljc gains many features in (#193):

    • kronecker and basis-unit for generating potentially infinite basis sequences
    • the ability to conj new items onto a structure: (conj (up 1 2) 3) => (up 1 2 3)
    • The structure-preserving map-chain takes a 2-arg function and presents it with each element of a deeply nested structure, along with a vector of its "chain", the path into its location. The fn's return becomes the new item at that location.
    • structure->prototype generates a same-shape structure as its argument, with symbolic entries that display their location (preserving orientation).
    • typical-object returns a structure of the same shape and orientation as s, generated by substituting gensymmed symbols in for each entry.
    • compatible-zero returns a structure compatible for multiplication with s down to 0.
    • transpose-outer returns a new structure with the same orientation as the first element of s, filled with elements of the same orientation as s. Each element is generating by taking the first element of each entry in s, the the second, etc... In that sense this is similar to a traditional matrix transpose.
    • dot-product takes the dot product of two structures. They must be the same top-level orientation and dimension; beyond that, their entries are pairwise-multiplied and summed.
    • inner-product is the same, but the left structure is conjugated first.
    • outer-product now works multiple levels deep.
    • vector-outer-product and vector-inner-product are similar, but only enforce the top-level length; all internal structures are NOT flattened and must be compatible for g/*.
    • compatible-for-contraction? now searches recursively down into a structure; previously it only checked the top level.
    • The new *allow-incompatible-multiplication* dynamic variable is set to true by default. Set it false to force a setting where, when you multiply structures, they must be:
      • opposite orientation
      • every element of the right entry must be compatible for contraction with the left
    • structure multiplication with scalars, etc now respects ordering, just in case any multiplication is not commutative.
    • sicmutils.generators now holds generators for up, down, and structure generators; these produce potentially deeply nested structures. up1, down1 and structure1 generate only one level deep. Mix and match! See structure_test.cljc for many examples of how to use these.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment