This release has been a long time coming, and introduces some substantial improvements to Emmy. The top-line items are:
Reverse-mode automatic differentiation
Forward-mode AD is now much simpler to understand; the implementation lives in emmy.dual. emmy.tape and emmy.autodiff round out our AD implementations.
Our reverse-mode AD implementation is faster for everything in the library, but substantially faster for functions with many inputs and few outputs (like Lagrangians). See the new D-forward, D-reverse, partial-forward and partial-reverse for more control over which you like.
Native Complex and Fraction
@littleredcomputer implemented our Fraction.js and Complex.js dependencies in pure CLJS, letting us remove these JS dependencies. This opens the door for symbolic complex numbers and more fun down the road.
Value protocol => multimethods
The emmy.value/Value protocol is now gone, in favor of a "multimethods everywhere" approach. If you want speed you can compile your functions... otherwise simple is better, and this keeps us closer to the scmutils style.
Excellent docs on https://2.maria.cloud
Thanks to @mhuebert, SCI now properly grabs all docstrings and arglists from all Emmy functions, making for an excellent user experience.
Faster compilation
@littleredcomputer (in #143) replaced thee the implementation of common subexpression elimination with the algorithm described in Flajolet's 1990 paper (cited within). This speeds up (and makes possible at all) compilation of some of the big, nasty functions we ran into generating manifolds in examples like https://sritchie.github.io/strange-loop-2023/notebooks/stl/pq_knot
All CHANGELOG entries
-
#170:
-
changes
Dandpartialto use reverse mode automatic differentiation by default, and fixes all associated tests -
adds
emmy.generic/{zero?,one?,identity?}implementations (all false) toemmy.tape/Completed, in case some collection type tries to simplify these during reverse-mode AD
-
-
#185:
-
adds a dynamic variable
emmy.calculus.derivative/*mode*that allows the user to switch between forward and reverse mode automatic differentiation -
adds a new
emmy.calculus.derivative/gradientthat acts likeemmy.tape/gradientbut is capable of taking multiple variables -
adds new operators
emmy.calculus.derivative/{D-forward, D-reverse}and operator-returning-functionsemmy.calculus.derivative/{partial-forward, partial-reverse}that allow the user to explicitly invoke forward-mode or reverse-mode automatic differentiation.Dandpartialstill default to forward-mode -
modifies
emmy.tape/gradientto correctly error when passed invalid selectors, just likeemmy.dual/derivative.
-
-
#183:
-
adds
emmy.{autodiff, tape}toemmy.sci's exported namespace set -
adds
emmy.dual/extract-idimplementations for all supported output types (every type that already implementsemmy.dual/IPerturbed) -
moves
emmy.tape/Completedtoemmy.dual/Completed; it doesn't really make sense there, but this is the only current way to remove the circular dependency betweenemmy.dualandemmy.tape. (tapeneeds adualimport to gainemmy.dual/IPerturbed.) -
simplifies the
emmy.tape/gradientimplementation down to only handle single real-or-structural arguments, just likeemmy.dual/derivative. We'll share the "handle-multiple-input" implementation between the two in a follow-up PR -
makes the tests in
emmy.calculus.derivativegeneric on the derivative implementation, so we can run all tests in forward and reverse mode.
-
-
#182:
-
moves the generic implementations for
TapeCellandDualtoemmy.autodiff -
moves
emmy.calculus.derivativetoemmy.dual/derivative -
removes
emmy.dual/perturbed?fromIPerturbed, as this is no longer used.
-
-
#180 renames
emmy.differentialtoemmy.dual, since the file now contains a proper dual number implementation, not a truncated multivariate power series. -
#179:
-
Moves the
IPerturbedimplementation for functions toemmy.function, out ofemmy.calculus.derivative -
Adds a new
modeparameter toemmy.differential/extract-tangent, in preparation for allowing reverse and forward mode across all output types
-
-
#175:
-
Adds
emmy.env/{tau,-tau}constants for the$\tau$ fans out there -
Adds
^:constmetadata to all constants, reaping small performance wins -
Updates
emmy.numerical.unimin.brent/{brent-min,brent-max,brent-min-commons,brent-max-commons}to:-
take a new
:initial-guessargument, useful if you have some idea of where the minimum might lie -
swaps the relative and absolute threshold defaults to match those from
scmutils -
adjusts the initial guess from the midpoint between
aandbto a golden section cut (closer toa), to matchscmutils
-
-
-
#156:
-
Makes forward- and reverse-mode automatic differentiation compatible with each other, allowing for proper mixed-mode AD
-
Adds support for derivatives of literal functions in reverse-mode
-
-
#165:
-
Fixes Alexey's Amazing Bug for our tape implementation
-
Adds the
idfield back intoTapeCell, required for the tag replacement machinery for fixing Alexey's Bug -
Adds an
emmy.differential/IPerturbedimplementation to TapeCell -
Fixes some old documentation referencing
:in->partials, plural -
Updates
emmy.tape/tag-ofto return nil in case of a non-tape argument vs##-Inf, again preparation for forward/reverse interactions -
Adds support for partial derivatives via a
selectorsargument togradient -
Fixes a bug in the
g/absimplementation forTapeCell, where(g/abs <tape>)would return a positive primal -
Adds a
simplifyimplementation forTapeCell
-
-
#163:
-
replaces the
emmy.differential.Differentialgeneralized dual and its term list algebra with a simplifiedemmy.differential.Dualnumber typeThis new approach works because the
emmy.differential/*active-tags*stack allows us to make sure that lifted binary operations always wrap their output in a newDualwith the tag assigned by the inner-most derivative call. -
deletes
emmy.util.vector-setand tests, as these are no longer used -
adds a
nilimplementation forextract-tangent, meaning thatnil-valued functions now work withD
-
-
#159:
-
Fixes
Differential's implementation ofemmy.value/numerical?to always returnfalse. The reason is thatnumerical?is used byg/*and friends to decide on simplifications like(* <dx-with-1> x) => x, which would lose the structure ofdx-with-1. By returning false we avoid these simplifications. -
Converts a number of
emmy.value/numerical?calls toemmy.value/scalar?. Thenumerical?protocol method is used only in generic functions likeg/*for deciding whether or not to apply numerical simplifications, like(* x 1) => x.Guarding on
v/scalar?instead allows us to let in numbers, tapes and differentials, since these latter two are meant to WRAP numbers, but should not be subject to numerical simplifications. -
Adds
emmy.structure/fold-chainfor performing a tree-like fold on structures, saving us work over the alternate pattern ofs/mapr,flattenandreduce.
-
-
-
Replace the implementation of arbitrary-precision rational arithmetic provided by
fraction.jswith Clojure code, allowing us to remove a JavaScript dependency. -
We note here a subtlety with fraction reader syntax. When the CLJS compiler runs and encounters input like
#emmy/ratio 1/2, since the compilation occurs in a JVM Clojure environment the 1/2 will deserialize as a Ratio, which is then transformed by the reader into code which will generate a ClojureScriptFraction. The ClojureScript reader, however, evaluates the input as q JS expression resulting in 0.5, which is then rationalized with an expensive algorithm which may lose exactness because of the unwanted floating-point conversion. For this reason, when we emit a ratio in string form, we quote the arguments:#emmy/ratio "1/2"to prevent the conversion. Consider this a deprecation notice for the unquoted form. We also now allow the form#emmy/ratio [1 2], like Complex, and now no longer allow an initial+on the numerator.
-
-
#154:
-
Adds
emmy.tapewith an implementation of reverse-mode automatic differentiation. The implementation is based on Alexey Radul's implementation in dvl, and seems to be higher-performance by quite a bit and capable of replacing our forward-mode implementation.-
The centerpiece of the implementation is
emmy.tape/gradient, which can handle$R^n \to R^m$ functions, as well as nested derivatives. -
All operations supported by [[emmy.differential/Differential]] are supported by the backing [[emmy.tape/TapeCell]] instance.
-
-
-
-
By porting
complex.jsto Clojure, we can remove the dependency on this library on the JavaScript side as well as the Apache Commons complex implementation on the JVM side.The JavaScript implementation is followed fairly closely and done with generic Emmy arithmetic at the component level (except when that is clearly unnecessary). The
(complex)constructor has been made equivalent to the reader parser.The former implementation made a special case of i^r, raising the complex unit to a real power, but it only worked for integral r, and threw an exception in other cases; this special case is removed.
-
-
-
Retires the Value protocol in favor of MultiFns in the generic scope. Doing this carefully, by revoking the existing implementation and then restoring it step by step, revealed some interesting corner cases:
-
(zero? [0])but(not (zero? (lazy-seq [0]))) (not (zero? (series 0)))
The only remaining element of the Value protocol was the
kindfunction, used to classify arguments for multi-dispatch. The protocol has therefore been renamed IKind. -
-
Changed behavior:
-
Objects produced by
make-literalIn the protocol regime, the implementation of
zero?was dispatched via the Literal class; with the MultiFn, it is dispatched by thekind, which is supplied by the creator. The protocol dispatch effectively usednumeric-zero?. That behavior is replicated for the kind:emmy.expression/numeric; if you create literals of a different kind, you can inherit this behavior usingderiveor supply adefmethodyourself. -
We now prefer to let Clojure internals report an exception whenever an unimplemented MultiFn in what was formerly the emmy.value/Value protocol instead of defining a method that throws. The exception thrown in such cases therefore changes from UnsupportedOperationException to IllegalArgumentException.
-
For this reason we have avoided defining
:defaulthandlers for generic MultiFns, despite the fact that defaults are certainly convenient in some cases (likefalseforexact?and having the default case forfreezepass through). -
The default exponentiation routine built on generic multiplication would, in the case of raising to the zero power, return the one-like of the exponent; now it returns the one-like of the base, which is correct.
-
The generic
negative?required Numbers instead of Comparables; this is fixed. -
Documentation string for
pochhammercorrected.
-
-
-
#145 (thank you to @mhuebert for amazing work here!!):
-
Adds
emmy.util/sci-macrofor defining macros meant to be exposed via SCI, without requiring redefinition as afn. -
Modifies
emmy.sciso that SCI has access to all of the metadata we need for a great experience on https://2.maria.cloud and other platforms that use SCI. -
Removes some unused
kondo/ignoremetadata and upgradesclj-kondoto 2023.07.13, which caught a couple more errors like a test with no assertion and a block of tests accidentally included in another test block. -
Upgrades
emmy.calculus.coordinate/define-coordinatesandemmy.util.def/{import-vars,careful-def}to play nicely with SCI. -
Resolves the ambiguous
simplifyimplementation for subvectors. -
Adds docstrings to the aliased macros in
emmy.env. -
Exposes
emmy.calculus.coordinate/coordinate-functionsasemmy.env/coordinate-functions.
-
-
#143:
-
Replace the implementation of common subexpression elimination with the algorithm described in Flajolet's 1990 paper (cited within). This algorithm does not have any dependence on hash collection ordering, so the
deterministic?flag has been removed. -
Update unit test expected data to remove symbol namespaces that may differ between clj and cljs. Restore a test found not to be stable in cljs that has become so with the simpler Flajolet algorithm.
-
The structure of the emitted code changes with this revision, to a more assembly-like form in which operations are fanned out into a sequence of two-operand forms in the commutative cases, boiling down to the return of a single symbol.
-
-
#142 fixes advanced compilation warnings and bumps Clerk to a new version.
New Contributors
- @ingydotnet made their first contribution in #171
- @yuhan0 made their first contribution in #172