Giter Club home page Giter Club logo

sicmutils's Introduction

SICMUtils

A Clojure(script) implementation of the scmutils system for math and physics investigations in the Clojure and ClojureScript languages. SICMUtils provides facilities for

And implementations of many different mathematical objects, all built on a tower of generic, extensible mathematical operations.

Scmutils is extensively used in the textbooks The Structure and Interpretation of Classical Mechanics and Functional Differential Geometry by G.J. Sussman and J. Wisdom.

👋 Need help getting started? Say hi on Twitter or Clojurians Slack in #sicmutils.

Build Status License Codecov branch cljdoc badge Clojars Project

Quickstart

SICMUtils is best experienced in an interactive environment like the REPL. We support many environments with rich support for TeX rendering and plotting.

Install SICMUtils into your Clojure(script) project using the instructions at its Clojars page:

Clojars Project

Initialize the sicmutils.env "Batteries Included" environment at the REPL:

(require '[sicmutils.env :as env])
(env/bootstrap-repl!)

See the demo directory for minimal examples of build configurations that use the SICMUtils library.

Alternatively, visit the SICMUtils Tutorial on Nextjournal to try all of the examples below in your browser with no setup required:

nje

Math works as expected (see Generics for the full menu of operations), but notice that the numeric tower includes complex numbers, and proper ratios in ClojureScript:

(- (* 7 (/ 1 2)) 2)
;;=> 3/2

(asin -10)
;;=> #sicm/complex [-1.5707963267948966 2.9932228461263786]

Symbols are interpreted as abstract complex numbers, and arithmetic on them generates symbolic expressions. You can render these with ->TeX and ->infix:

(def render (comp ->infix simplify))

(square (sin (+ 'a 3)))
;;=> (expt (sin (+ a 3)) 2)

(render (square (sin (+ 'a 3))))
;;=> "sin²(a + 3)"

Use the D operator to perform forward-mode automatic differentiation and simplify to collapse symbolic expressions into tidy form:

((D cube) 'x)
;;=>  (+ (* x (+ x x)) (* x x))

(simplify ((D cube) 'x))
;;=> (* 3 (expt x 2))

(->infix
 (simplify ((D cube) 'x)))
;;-> "3 x²"

SICMUtils is based on the engine behind The Structure and Interpretation of Classical Mechanics, and has a built-in API for exploring Lagrangian and Hamiltonian mechanics.

Define a Lagrangian for a central potential U acting on a particle with mass m:

(defn L-central-polar [m U]
  (fn [[_ [r] [rdot thetadot]]]
    (- (* 1/2 m
          (+ (square rdot)
             (square (* r thetadot))))
       (U r))))

and generate the two Euler-Lagrange equations of motion for the r and theta coordinates:

(let [potential-fn (literal-function 'U)
      L     (L-central-polar 'm potential-fn)
      state (up (literal-function 'r)
                (literal-function 'theta))]
  (render
   (((Lagrange-equations L) state) 't)))
;;=> "down(- m r(t) (Dθ(t))² + m D²r(t) + DU(r(t)), m (r(t))² D²θ(t) + 2 m r(t) Dr(t) Dθ(t))"

There is so much more! This is a dense library, and lots of documentation remains to be written. Some suggested next steps, for now:

Background

SICM and FDG can be thought of as spiritual successors to The Structure and Interpretation of Computer Programs, a very influential text—as I can attest, since carefully reading this book in my 30s changed my life as a programmer. To see the same techniques applied to differential geometry and physics is an irresistible lure.

Scmutils is an excellent system, but it is written in an older variant of LISP (Scheme) and is tied to a particular implementation of Scheme—MIT/GNU Scheme. (There is a port to Guile, but due to the fact that Guile does not support MIT Scheme's apply hooks some glue code is required to run examples from the book in that environment.)

Having the system in Clojure offers a number of advantages. It is not necessary to obtain or prepare a MIT/GNU Scheme executable to execute: only a Java runtime is required. It does not require the X Window System for graphics, as MIT Scheme does. All of the standard tooling for Java and Clojure become available, and this is a lot compared to what we get with MIT/GNU scheme. Clojure support is now extensive in any number of editors and IDEs. Even better, you can interact with the system in the context of a Jupyter notebook.

You can invoke the system from within Java or Javascript code or use any Java or JS packages you like together with the mathematics system. It's my hope that continuing this project will extend the reach of SICM and FDG by allowing experimentation and collaboration with them in modern environments.

Citing SICMUtils

To cite this repository, see the "Cite this Repository" link on the top right of the Github page. Citation information is generated from CITATION.cff.

Here is the generated BibTeX entry:

@software{Ritchie_SICMUtils_Functional_Computer_2016},
author = {Ritchie, Sam and Smith, Colin},
license = {GPL-3.0},
month = {4},
title = {{SICMUtils: Functional Computer Algebra in Clojure}},
url = {https://github.com/sicmutils/sicmutils},
version = {0.23.0},
year = {2016}

In the above BibTeX entry, the version number is intended to be that from project.clj, and the year corresponds to the project's open-source release.

License

GPL v3.

Copyright © 2016 Colin Smith

sicmutils's People

Contributors

adamhaber avatar alexgian avatar blak3mill3r avatar borkdude avatar eightysteele avatar hcarvalhoalves avatar jonasemueller avatar kloimhardt avatar littleredcomputer avatar mk avatar pangloss avatar scgilardi avatar sigmaxipi avatar sritchie avatar swhalen avatar teodorlu avatar zane avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

sicmutils's Issues

Composition of operators

Scmutils defines the product of operators (D, partial[pd in clojure]) as their compositon.
This has been correctly implemented by the "expt" function. So,

> (defn ff[x y z] (+ (* x x y)(*y y z)(* z z x))) => ff
> (((expt D 2) ff)'x 'y 'z)
(down
 (down (* 2 y) (* 2 x) (* 2 z))
 (down (* 2 x) (* 2 z) (* 2 y))
 (down (* 2 z) (* 2 y) (* 2 x)))

great, so far - pd is also OK:

> (((expt (pd 0) 2) ff)'x 'y 'z)
(* 2 y)

However, it is not possible to use "*",

> (((* D D) ff)'x 'y 'z)
ArityException Wrong number of args (3) passed to: operator/mul/fn--2789/fn--2790  clojure.lang.AFn.throwArity (AFn.java:429)

or even "compose":

(((compose D D) ff)'x 'y 'z)
AbstractMethodError net.littleredcomputer.math.operator.Operator.arity()Ljava/lang/Object;  net.littleredcomputer.math.function/compose (function.clj:285)

While this is not really a problem for "D", since all the uses are equivalent, it does cause problems with "pd". Suppose I need to find ∂²f/∂x∂y

(((compose (pd 0)(pd 1)) ff) 'x 'y 'z)
AbstractMethodError net.littleredcomputer.math.operator.Operator.arity()Ljava/lang/Object;  net.littleredcomputer.math.function/compose (function.clj:285)

IOW, in the example above, using ((expt (pd [0|1|2]) 2) ff) I could only compute ∂²ff/∂x², ∂²ff/∂y² or ∂²ff/∂z² and not ∂²ff/∂y∂z or whatever, never mind ∂³ff/∂x∂y∂z. We need to be able to combine operators using "*" or "compose", as in MIT-Scheme.

I won't even mention application of vector operators as dot-products, cross-products, etc. yet, I'm trying some simplistic implementations of vector calculus of my own. I know that Scmutils does have its own vector calculus routines, they seem very generalised (based on Spivak, I think). I do not know if grad, div and curl are implemented as composable vector operators there, but I would be surprised if they're not*. I'd want to be sure everything works on a basic level first, before having a stab at that!


PS

  • They're not. I was surprised.

Comments on https://github.com/littleredcomputer/sicmutils/blob/master/src/sicmutils/numerical/interpolate/polynomial.cljc

General comments:

  1. This is a great essay for someone who is motivated to learn both Clojure and the subject. There's less handholding than I would have put in for a pedagogical blog post, but maybe that's intentional to force the reader to work some stuff out on their own?
    For example, in line 175, talking about building the tableau, you write "If each cell in the above tableau tracked ... " If I were writing this, I would have written more elaborate, something like "Notice what this expression means: If we know the values of the Left and Right polynomials, evaluated at our point of interest, and we also know the x-coordinates of the leftmost and rightmost points, then that is all we need to determine the value of our next-degree polynomial at our point of interest. This is just a generalization of the expression in line 82, for computing the y-coordinate of a line connecting the points (x1, y1) and (x2, y2)." And then I would have put a picture there.

  2. Related, the essay was a bit tough to follow for someone who isn't very familiar with Clojure. Not sure how to get around that, but if the target audience isn't going to be Clojure experts, they might have a tough time. For example, I stared at the Lagrange function for a long time trying to figure out how it worked (and fired up Maria to test some ideas), and still don't perfectly understand what it's doing. With some time investment (on the order of a couple hours, I'd guess) I can (and will) go through and understand everything in detail, but some readers might not be motivated to do that.

  3. More motivation at the top, for why polynomial interpolation is worth studying, would be nice. Is it just that the subject is intrinsically interesting, or is there a need for it in SICM? If so, some example uses in the library would be good.

  4. If I understood the flow correctly, we have Lagrange's method first, but this suffers from two drawbacks, only one of which is addressed by Neville's algorithm: if I add a new point to the set of points, I have to do everything all over again. Might be nice to emphasize this after the Neville's algorithm function.

Smaller comments:

Tableau-based methods (line 134): make it clear that the tableau is going left -> right, not top -> bottom

Typo (?) in 164-166: "Each entry in each successive column is generated through some operation between the entry to its left, and the entry one left and one up." shouldn't up be down?
Same typo is in L173, if this was correct.

Line 75, latex error

More to come!

Make derivative open-dispatch by converting its implementation to protocols

I don't know the implementation well yet, but I wanted to flag this section as ripe for protocol conversion. https://github.com/littleredcomputer/sicmutils/blob/master/src/sicmutils/calculus/derivative.cljc#L218

the generic arithmetic is totally open now. I think a nice test would be - can someone implement Quaternions in their code and have everything just work, given some set of protocol and multimethod implementations?

Making a ticket to get your thoughts, @littleredcomputer , and to track notes on how we might make this happen.

Apparent missing multimethod(s)

Hello,

First of all, thank you for taking the time to write this software. I have been interested in SICM/scmutils for some time and I am just getting around to working through the book now, in no small part thanks to your efforts in porting scmutils to Clojure.

In working through some of the early examples in the book, I came across an error which can be minimally reproduced as follows:

[sicmutils.env] > (* (/ 'x 'y) 2.0)

IllegalArgumentException No method in multimethod 'mul' for dispatch value: [:sicmutils.rational-function/rational-function java.lang.Double]  clojure.lang.MultiFn.getFn (MultiFn.java:156)

This might be related to #24.

Best regards,
Simon Whalen

Polynomials use `numsymb` directly for simplification vs generic ops

I'll go ahead and call this a bug until I'm corrected!

  • Polynomial currently calls methods from sicmutils.numsymb like sym/add, etc directly. I think we can have the polynomial methods call the fully generic g/add etc if we're more careful with our types. This would let us move some of the optimizations around numsymb without breaking code. #47 ran into trouble because of this, for example.

extend make-rectangular, make-polar to structures, matrices

This would give us a nice way to build complex matrices and structures (provided compatible structures). This complements #330 , which adds real-part and imag-part implementations for these types.

scmutils does not have support for this, but matlab's implementation works great with compound structures.

Clojure 1.9.0 + Spec compatibility

For correct compilation with Clojure 1.9.0 + Spec, I suggest to fix three colons:

series.clj, line 163
(derive ::sicmutils.expression/numerical-expression ::coseries)
(derive :sicmutils.expression/numerical-expression ::coseries) ;; <- suggested

rule.clj, line 21
(require [pattern.match :refer :all]))
(:require [pattern.match :refer :all])) ;; <- suggested

function.clj, line 217
(derive ::function ::sicmutils.series/coseries)
(derive ::function :sicmutils.series/coseries) ;; <- suggested

multi-arity functions break "arity" on the JVM

sicmutils.function/arity currently fails on calls like

(defn cake
  ([x] x)
  ([x y] x))

(arity cake)
Execution error (IllegalArgumentException) at sun.reflect.GeneratedConstructorAccessor2/newInstance (REPL:-1).
arity? sicmutils.function$cake@63b30ba3 {:invoke [[:invoke 2] [:invoke 1]]}

This works in CLJS due to this section: https://github.com/littleredcomputer/sicmutils/blob/master/src/sicmutils/function.cljc#L206

We want to make the JVM version work the same by handing (:invoke facts) with multiple entries here: https://github.com/littleredcomputer/sicmutils/blob/master/src/sicmutils/function.cljc#L145

Bit of attention needed in the multimethods for rational-function?

This was spotted while not running off "lein repl"
I fed in the following

(defn f2 [x y] (* (sin x) (log y)))
((D f2)'x 'y)
(simplify ((D f2)'x 'y))
(defn f3 [x y] (* (tan x) (log y)))
((D f3)'x 'y)
(simplify ((D f3)'x 'y))
(defn f4 [x y] (* (tan x) (sin y)))
((D f4)'x 'y)
(simplify ((D f4)'x 'y))
(defn f5 [x y] (/ (tan x) (sin y)))
((D f5)'x 'y)
(simplify((D f5)'x 'y))

they all seem to have differentiated properly, but f3 and f5 threw exceptions when it came to formatting.

#'fluttering-dusk/f2
#object[sicmutils.structure.Struct 0x687e067b "(down (* (cos x) (log y)) (* (sin x) (/ 1 y)))"]
(down (* (cos x) (log y)) (/ (sin x) y))

#'fluttering-dusk/f3
#object[sicmutils.structure.Struct 0x5bc5db88 "(down (* (/ 1 (expt (cos x) 2)) (log y)) (* (tan x) (/ 1 y)))"]
<< Exception thrown here >>

#'fluttering-dusk/f4
#object[sicmutils.structure.Struct 0x3fb8165 "(down (* (/ 1 (expt (cos x) 2)) (sin y)) (* (tan x) (cos y)))"]
(down (/ (sin y) (expt (cos x) 2)) (/ (* (cos y) (sin x)) (cos x)))

#'fluttering-dusk/f5
#object[sicmutils.structure.Struct 0x3800b7ff "(down (* (/ 1 (expt (cos x) 2)) (/ 1 (sin y))) (* (- (/ (tan x) (expt (sin y) 2))) (cos y)))"]
<< Exception thrown here >>

This is the exception that was thrown

Exception thrown: java.lang.IllegalArgumentException (No method in multimethod 'div' for dispatch value: [:sicmutils.rational-function/rational-function :sicmutils.polynomial/polynomial])

I haven't looked at the code, but it seems that it's just the relevant defmulti missing.
I hope that's some use towards patching it.

Need to define "ref" as index for structures

Even though it's a Clojure function, perhaps it can be generalised somehow - by arg type, perhaps? -, or shimmed in the manner of "partial".

Even though it's only referenced twice in SICM (fn p.17 and "Our Notation" p.494) it is used a lot in FDG.

add proper zero? and one? implementations for Differential`

Here's what we have in scmutils:

(comment
  (define (diff:zero? x)
    (assert (differential? x))
    (for-all? (differential-term-list x)
              (lambda (term)
                      (let ((c (differential-coefficient term)))
                        (g:zero? c)))))

  ;; TODO fix this for differential one and identity?
  (define (diff:one? x)
    (assert (differential? x))
    (and (g:one? (finite-part x))
         (diff:zero? (infinitesimal-part x))))
  )

maybe we can get here by overriding equality.

But right now, this is the implementation:

(zero? [_] (every? v/zero? (map coefficient terms)))
  (one? [_] false)

So I think we can do better here!

GPL v3 is incompatible with EPL

Thanks for your great work. I think it might be useful in implementing automatic differentiation for neural networks. I have also started to explore examples from FDG. It has popped up a few times already, e.g. https://ujihisa.wordpress.com/2013/12/11/releasing-clojure-applications-with-gplv3/. Could you consider relicencing in compatible fashion? Since scmutils is under GPL appearingly, you could add a clause that excludes all dependencies. I am not a legal expert though (maybe the FSF can help).

2nd partial derivative of literal function

> (def ff (literal-function 'ff [0 0] 0))
> (((* D D) ff) 'x 'y)        ;; great, no problems
(down
 (down (((∂ 0) ((∂ 0) ff)) x y) (((∂ 0) ((∂ 1) ff)) x y))
 (down (((∂ 1) ((∂ 0) ff)) x y) (((∂ 1) ((∂ 1) ff)) x y))) 
> (((* (partial 1) (partial 0)) ff) 'x 'y)  ;; composition of partials, even better
(((∂ 1) ((∂ 0) ff)) x y)

but plain invocation via selectors...

> (((partial 1 0) ff) 'x 'y)    ;; ........erm?
IllegalArgumentException No implementation of method: :numerical? of protocol: #'sicmutils.value/Value found for class: nil  clojure.core/-cache-protocol-fn (core_deftype.clj:554)

A couple of issues here (D cot)

I've included both issues here, probably should make two separate issues, sorry...

First,

[sicmutils.env] > (defn cot [x] (/ 1 (tan x)))
cot
[sicmutils.env] > ((D cot)'x)
(/ -1 (* (expt (tan x) 2) (expt (cos x) 2)))

The simplifier is lagging a bit here, yes?
scmutils gives a somewhat better

1 ]=> ((D cot) 'x)
#|
(/ -1 (expt (sin x) 2))
|#

We also find other similar fruity examples, such as:

[sicmutils.env] > ((D (/ cos sin)) 'x)
(/ 1 (+ (expt (cos x) 2) -1))

They're not wrong, of course, but their simplification might be a bit more elegant.

Secondly, when we get to the generated TeX code, we get:

$$\dfrac-1{{\tan\left(x\right)}^2\,{\cos\left(x\right)}^2}$$

Which gives a bit of a garbled result.
see here:
http://aegean.pwp.blueyonder.co.uk/images/cool/eclipse2.png

I suspect something like the following would fix it a bit

$$\dfrac{-1}{{\tan\left(x\right)}^2\,{\cos\left(x\right)}^2}$$

At least it does on the Gorilla-REPL renderer which I'm using to check.

The REPL experience is not polished.

  1. When running with lein repl, simplification and prettyprinting are not applied to expression results automatically. (One can wrap expressions with (print-expression) as a workaround.)

  2. When running using lein run -m net.littleredcomputer.math.repl, the printing is right, but the command line editing keys do not work.

(We should let lein repl do the work. The trick is to figure out how to intercept values before printing. This looks like a job for nrepl middleware, something I know nothing about, so I'll learn something new. If that pans out, I'll get rid of the repl class).

Exponentation not defined on Complex

(def pi (* 2 (asin 1))) ;; do we have any pre-defined constants, BTW? couldn't see any.
(def e (exp 1))
(def i (complex 0 1))
(expt e (* i pi))

IllegalArgumentException No method in multimethod 'expt' for dispatch value: [java.lang.Double :net.littleredcomputer.math.complex/complex] clojure.lang.MultiFn.getFn (MultiFn.java:156)

No `div` impl between numbers and polynomials

The following code, in a namespace with the proper imports like sicmutils.simplify-test:

(sicmutils.simplify/hermetic-simplify-fixture
 (fn []
   (g/simplify
    (g/mul 0.5
           (g/div 1 (g/mul 2 'x))))))

Throws:

Execution error (Error) at (<cljs repl>:1).
No method in multimethod 'sicmutils.generic/div' for dispatch value: [#object[Number] :sicmutils.polynomial/polynomial]

Everything works if we use (/ 1 2) to generate a ratio. Do we want to handle a floating point number here?

Well, of course we do. I guess I would expect here that maybe the simplifier would do nothing, and return (/ 0.5 (* 2 x)).

Improve / cache characteristic polynomial computation

A note from @littleredcomputer on a previous PR:

Before I forget: I was looking at this and thinking, "it would be cool if (characteristic-polynomial M) returned a function of x that was polynomial in x, without having to name it. Of course, that is easily done, by just having the function return (fn [x] ...), but then it does a (potentially vast) amount of computation every time you call it. If only there were a way to record that computation in a way that could be played back on 'x or 3. I think that is something we should think about; it would have application to our numerics and to differentiation itself.

One way to do this that would work today would be to pass a symbol, then simplify and compile the resulting symbolic expression:

(defn characteristic-fn [M]
  (compile/compile-univariate-fn
   (partial m/characteristic-polynomial M)))

This would NOT, unlike your suggestion, be able to handle 'x or 3, just 3, since we compile the native math functions in.

But we COULD make an alternative sci-context for the compile namespace that would stick the generics back in, instead of the real versions. @littleredcomputer would this be a good thing?

Before I thought of this approach, I had found a presentation called "Efficient Computation of the Characteristic Polynomial", so I'll include that here for reference.

ch6 tests behave differently depending on seemingly equivalent series g/-

Here's a reproduction. You can make this test fail by redefining the existing seq:- in series.cljc:

(defn- seq:- [f g]
  (seq:+ f (seq:negate g)))

to:

(defn- seq:- [f g]
  (map g/- f g))

Which should be equivalent. Here's the check:

(deftest ch6-repro
  "Evaluate this in sicmutils.sicm.ch6_test.cljc. If you defined g/- for series
  as `(map g/- f g)`, this test will return a different result than if you
  define it as `(g/+ f (g/negate g))`."
  (let [L (e/Lie-derivative
           (fn [[_ theta ptheta]]
             (/ (* -1 'a 'b (sin theta))
                ptheta)))
        H (e/series
           (fn [[_ _ ptheta]]
             (/ (square ptheta)
                (* 2 'a)))
           (fn [[_ theta _]]
             (* -1 'e 'b (cos theta))))
        E (((exp (* 'e L)) H) (up 't 'theta 'p_theta))]
    (is (= '((/ (expt p_theta 2) (* 2 a))
             0
             (/ (* a (expt b 2) (expt e 2) (expt (sin theta) 2))
                (* 2 (expt p_theta 2))))
           (take 3 (simplify E))))))

With the existing definition, the 3rd sequence entry is

(/ (* a (expt b 2) (expt e 2) (expt (sin theta) 2)) (* 2 (expt p_theta 2)))

If you use the new g/- based definition, you pick up a factor of 3 in the third term:

(/ (* 3N a (expt b 2) (expt e 2) (expt (sin theta) 2)) (* 2 (expt p_theta 2)))

Seems like a bug with the simplifier.

Add cljdoc build

CLJDoc has a build already for our API Docs: https://cljdoc.org/d/net.littleredcomputer/sicmutils/0.12.1

I pushed a change that will let the site find the git repo, so the readme should show up next time we make a release. It would be nice to add a loose table of contents and some documentation that we can begin to flesh out. Here's the guide: https://github.com/cljdoc/cljdoc

Another option is https://github.com/oakes/Dynadoc. This lets you embed actual runnable examples in the documentation. This could be incredible, now that we're fully cljs compatible.

Chapter 1.4

Trying
(defn q [ ] (find-path (L-harmonic 1.0 1.0) 0.0 1.0 1.57 0.0 3))

Then
((q) 0.0) => 1.0
which is correct
but
((q) 0.1)
getting
IllegalArgumentException No method in multimethod 'mul' for dispatch value: [java.lang.Double [D] clojure.lang.MultiFn.getFn (MultiFn.java:156)

bug in radical simplifier

Here's the result of Scheme:

(->tex-equation
 (+ (sqrt (* (expt 'y_1 2) (/ (+ (* (expt 'x_2 2) (expt 'y_1 2)) (expt 'y_1 4) (* 2 (expt 'y_1 3) 'y_2) (* (expt 'y_1 2) (expt 'y_2 2))) (+ (expt 'y_1 2) (* 2 'y_1 'y_2) (expt 'y_2 2)))))
    (sqrt (* (expt 'y_2 2) (/ (+ (* (expt 'x_2 2) (expt 'y_1 2)) (expt 'y_1 4) (* 2 (expt 'y_1 3) 'y_2) (* (expt 'y_1 2) (expt 'y_2 2))) (+ (expt 'y_1 2) (* 2 'y_1 'y_2) (expt 'y_2 2)))))))

image

and in Clojure:

image

add solve-linear, solve-linear-{left,right} generic functions from kernel

We are missing these three generic functions. If we get these, we have ALL of the generics (except for #191 !)

(def-generic-function solve-linear 2)
(def-generic-function solve-linear-left 2)
(def-generic-function solve-linear-right 2)

These are implemented in scmutils for

  • power series
  • numbers
  • literal expressions ("abstract numbers")
  • structures, vectors (ie up structures)
  • quaternions
  • operators

and also these types... these aren't special, I just went to the trouble of digging up the scheme:

Differentials

(assign-operation 'solve-linear-right     diff:/ differential? not-compound?)
(assign-operation 'solve-linear-right     diff:/ not-compound? differential?)
(assign-operation 'solve-linear-left      (lambda (x y) (diff:/ y x)) not-compound? differential?)
(assign-operation 'solve-linear-left      (lambda (x y) (diff:/ y x)) differential? not-compound?)
(assign-operation 'solve-linear           (lambda (x y) (diff:/ y x)) not-compound? differential?)
(assign-operation 'solve-linear           (lambda (x y) (diff:/ y x)) differential? not-compound?)

Functions

This requires only:

(defbinary g/solve-linear)
(defbinary g/solve-linear-left)
(defbinary g/solve-linear-right)

ModInt

(assign-operation 'solve-linear-right  mod:/                  modint?  modint?)
(assign-operation 'solve-linear-left   (lambda (x y) (mod:/ y x))  modint?  modint?)
(assign-operation 'solve-linear        (lambda (x y) (mod:/ y x))  modint?  modint?)

Matrices

This is the bigger one. Installation:

(assign-operation 'solve-linear-right m:rsolve       row-matrix? square-matrix?)
(assign-operation 'solve-linear-right m:rsolve       down?       square-matrix?)

(assign-operation 'solve-linear-left m:solve-linear  square-matrix? column-matrix?)
(assign-operation 'solve-linear-left m:solve-linear  square-matrix? up?)

(assign-operation 'solve-linear m:solve-linear       square-matrix? column-matrix?)
(assign-operation 'solve-linear m:solve-linear       square-matrix? up?)
(assign-operation 'solve-linear m:solve-linear       square-matrix? row-matrix?)
(assign-operation 'solve-linear m:solve-linear       square-matrix? down?)

Some implementation hints, all from matrices.scm:

(define (Cramers-rule add sub mul div zero?)
  (let ((det (general-determinant add sub mul zero?)))
    (define solve
      (lambda (A B)
	(assert (and (matrix? A)
		     (column-matrix? B)
		     (fix:= (m:dimension A) (m:num-rows B))))
	(let ((bv (m:nth-col B 0))
	      (d (det A))
	      (At (m:transpose A)))
	  (vector->column-matrix
	   (make-initialized-vector (vector-length bv)
	     (lambda (i)
	       (div (det (matrix-with-substituted-row At i bv))
		    d)))))))
    solve))

(define solve-general
  (Cramers-rule g:+ g:- g:* g:/ easy-zero?))

(define (m:solve A b)
;; This seems to be a variable that is set to false by default, where `solve-numerical` is some implementation you can install.
  (if numerical?
      (solve-numerical A b)
      (solve-general A b)))

(define (m:rsolve b A)
  (cond ((up? b)
         (column-matrix->up
          (m:solve A (up->column-matrix b))))
        ((column-matrix? b)
         (m:solve A b))
        ((down? b)
         (row-matrix->down
          (m:transpose
           (m:solve (m:transpose A)
                    (m:transpose (down->row-matrix b))))))
        ((row-matrix? b)
         (m:transpose
          (m:solve (m:transpose A)
                   (m:transpose b))))
        (else (error "I don't know how to solve:" b A))))

(define (m:solve-linear A b)
  (m:rsolve b a))

nrepl under macOS not working

Just read your posting in hacking news and trying.

So far the uberjar work except the release no. is now 0.12

"java -jar target/uberjar/sicmutils-0.12.0-SNAPSHOT-standalone.jar < demo.clj"

and test ok:

`
2019-04-28 11:18:21.648 WARNING sicmutils.polynomial-factor Assuming (non-negative? I) in root-out-squares

lein test sicmutils.sicm.ch6-test

lein test sicmutils.sicm.ch7-test

lein test sicmutils.simplify-test

lein test sicmutils.structure-test

lein test sicmutils.tex-web-test

lein test sicmutils.value-test

Ran 186 tests containing 1815 assertions.
0 failures, 0 errors.

`

But not the nrepl even after I add the dependence of [nrepl "0.6.0"] there. Not sure how to resolve this.

`
lein repl < demo.clj
[WARNING] No nREPL middleware descriptor in metadata of #'sicmutils.repl/math-printer, see nrepl.middleware/set-descriptor!
nREPL server started on port 58542 on host 127.0.0.1 - nrepl://127.0.0.1:58542
ERROR: Unhandled REPL handler exception processing message {:id c6311d23-bcc6-4f15-81ff-400678d0f83a, :op clone}
java.lang.IllegalArgumentException: No implementation of method: :send of protocol: #'nrepl.transport/Transport found for class: sicmutils.repl$math_printer$fn$reify__10495
at clojure.core$cache_protocol_fn.invokeStatic(core_deftype.clj:583)
at clojure.core$cache_protocol_fn.invoke(core_deftype.clj:575)
at nrepl.transport$eval9398$fn__9399$G__9389__9406.invoke(transport.clj:16)
at nrepl.middleware.print$send_nonstreamed.invokeStatic(print.clj:157)
at nrepl.middleware.print$send_nonstreamed.invoke(print.clj:138)
at nrepl.middleware.print$printing_transport$reify__9891.send(print.clj:174)
at nrepl.middleware.caught$caught_transport$reify__9926.send(caught.clj:58)
at nrepl.middleware.session$register_session.invokeStatic(session.clj:205)
at nrepl.middleware.session$register_session.invoke(session.clj:197)
at nrepl.middleware.session$session$fn__10123.invoke(session.clj:267)
at nrepl.middleware$wrap_conj_descriptor$fn__9678.invoke(middleware.clj:16)
at nrepl.middleware.caught$wrap_caught$fn__9935.invoke(caught.clj:97)
at nrepl.middleware$wrap_conj_descriptor$fn__9678.invoke(middleware.clj:16)
at nrepl.middleware.print$wrap_print$fn__9902.invoke(print.clj:234)
at nrepl.middleware$wrap_conj_descriptor$fn__9678.invoke(middleware.clj:16)
at sicmutils.repl$math_printer$fn__10493.invoke(repl.clj:39)
at clojure.tools.nrepl.middleware$wrap_conj_descriptor$fn__1107.invoke(middleware.clj:22)
at nrepl.server$handle_STAR
.invokeStatic(server.clj:18)
at nrepl.server$handle_STAR
.invoke(server.clj:15)
at nrepl.server$handle$fn__10160.invoke(server.clj:27)
at clojure.core$binding_conveyor_fn$fn__5476.invoke(core.clj:2022)
at clojure.lang.AFn.call(AFn.java:18)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)

`

I add this to the project.cly btw,

`

  •           [potemkin "0.4.3"]]
    
  •           [potemkin "0.4.3"]
    
  •       	 [nrepl "0.6.0"]]
    

`

Just in case I do a "lein new app testapp" and it does launch the nrepl and hence may be even the additional dependence is not needed.

'
lein repl
nREPL server started on port 59408 on host 127.0.0.1 - nrepl://127.0.0.1:59408
REPL-y 0.4.3, nREPL 0.6.0
Clojure 1.10.0
Java HotSpot(TM) 64-Bit Server VM 1.8.0_45-b14
Docs: (doc function-name-here)
(find-doc "part-of-name-here")
Source: (source function-name-here)
Javadoc: (javadoc java-object-or-class-here)
Exit: Control+D or (exit) or (quit)
Results: Stored in vars *1, *2, *3, an exception in *e

testapp.core=> (+ 1 2)
3
testapp.core=> Bye for now!

'

There must be something in the code that trigger the error message.

Multimethods for sub, add, div, expt need some attention re structures

As it should be: (mul)

[sicmutils.env] > (* (up 'a 'b) 'c)
(up (* a c) (* b c))
[sicmutils.env] > (* 'a (up 'b 'c))
(up (* a b) (* a c))

Not quite as good (div)

(up (* a b) (* a c))
[sicmutils.env] > (/ (up 'a 'b) 'c)
(up (/ a c) (/ b c))
[sicmutils.env] > (/ 'a (up 'b 'c))

IllegalArgumentException No method in multimethod 'div' for dispatch value: [clojure.lang.Symbol :sicmutils.structure/up]  clojure.lang.MultiFn.getFn (MultiFn.java:156)

Could be better (add, expt)

[sicmutils.env] > (+ (up 'a 'b) 'c)

IllegalArgumentException No method in multimethod 'add' for dispatch value: [:sicmutils.structure/up clojure.lang.Symbol]  clojure.lang.MultiFn.getFn (MultiFn.java:156)
[sicmutils.env] > (+ 'a (up 'b 'c))
IllegalArgumentException No method in multimethod 'add' for dispatch value: [clojure.lang.Symbol :sicmutils.structure/up]  clojure.lang.MultiFn.getFn (MultiFn.java:156)

[sicmutils.env] > (expt (up 'a 'b) 'c)
IllegalArgumentException No method in multimethod 'expt' for dispatch value: [:sicmutils.structure/up clojure.lang.Symbol]  clojure.lang.MultiFn.getFn (MultiFn.java:156)

[sicmutils.env] > (expt 'a (up 'b 'c))

IllegalArgumentException No method in multimethod 'expt' for dispatch value: [clojure.lang.Symbol :sicmutils.structure/up]  clojure.lang.MultiFn.getFn (MultiFn.java:156)

sub gives two different messages

[sicmutils.env] > (- (up 'a 'b) 'c)

IllegalArgumentException No method in multimethod 'sub' for dispatch value: [:sicmutils.structure/up clojure.lang.Symbol]  clojure.lang.MultiFn.getFn (MultiFn.java:156)
[sicmutils.env] > (- 'a ('b 'c))

IllegalArgumentException No implementation of method: :nullity? of protocol: #'sicmutils.value/Value found for class: nil  clojure.core/-cache-protocol-fn (core_deftype.clj:554)

Generic functions don't print nicely.

Evaluating something like (up sin cos) produces

(up
#object[clojure.lang.MultiFn 0x2c8c16c0 "clojure.lang.MultiFn@2c8c16c0"]
#object[clojure.lang.MultiFn 0x329bad59 "clojure.lang.MultiFn@329bad59"])

when printed. It's correct from Clojure's point of view, but ugly. Perhaps the standard generic functions could be tagged with metadata that would suggest a prettier print format that (print-expression) could then substitute.

"compose" should be available

The compose function as found in math/function.clj should be available at the top-level REPL as it is often used in Sussman's texts.

You need beer

Post a BTC address and ill send you one :)

Thank You

Question: Can I assume that (value/= (simplify x) (simplify y)) "works" for all cases?

Hello!

Quick question. I'm observing that

(value/= (simplify x) (simplify y))

"works" for my simple cases, like

(v/= (g/simplify (g/* 'x 'y))
     (g/simplify (g/* 'y 'x)))
;; => true

. But I'm hesitating to make this assumption general.

  • Are there known counter-examples where (value/= (simplify x) (simplify y)) should identify equality, but doesn't find it?
    • Does simplify "bottom out" to a canonical form?
  • Are there property tests for simplify? I'm just finding example tests in sicmutils.simplify-test.

Thanks!

Generic functions - further improvement to formatting

MIT ScmUtils prints as follows when a (symbolic) derivative of a function is computed:

> (D sin)
#| a-euclidean-derivative |#

Further, when a function is defined or composed, we have the slightly off-putting

> (define (slope x y) (/ y x))
slope
> slope
#| (??? arg) |#
> (compose sin cos)
#| (??? arg) |#

I think (??? arg) is just its way of saying "unknown function", but I've seen people who think they've made an error on seeing that.

Here, we have:

 > (defn slope [x y] (/ y x))
slope
 > slope
#object[net.littleredcomputer.math.env$slope 0x4a2b11c0 "net.littleredcomputer.math.env$slope@4a2b11c0"]

Could the formatter make this say "slope"? its already in the string.

> (compose sin cos)
#object[clojure.lang.AFunction$1 0xee78f7b "clojure.lang.AFunction$1@ee78f7b"]
> (D (compose sin cos))
#object[clojure.lang.AFunction$1 0x1d03869a "clojure.lang.AFunction$1@1d03869a"]

Could the formatter say "A generated/composed function" or whatever?
I don't think the system can distinguish between the two cases (derivative or plain) like ScmUtils can? Can it? Even if not, something pretty in the output would be nice.

Add a generic "compare" interface for comparison between numbers, abstract #s, differential

In the original scmutils, GJS implements the various comparison operators between Differential and numbers. This allows automatic differentiation to proceed through conditional statements.

This example silently returns "false" in the conditional:

(let [f (fn [x]
          (prn x (< x 10) (< 10 x))
          (if (< 10 x)
            (square x)
            (cube x)))]
  ((D f) 5))
;; returns 75
;; prints: D[[] → 5 [17] → 1] false false

We should also allow statements like:

(< 10 (literal-number 12))
;; should return true, returns false. ALSO returns false in scheme!

(< (literal-number 10) 12)
;; this returns true in Clojure (correctly), false in Scheme.

Making comparisons generic would also allow us to implement comparisons for rationals and bigints in the CLJS numeric tower.

invert

Could we have g/invert promoted into env.clj please (as per scmutils, as ever)

port Quaternions over from scmutils, alternate CLJ approaches

Scheme implementation, for reference: https://github.com/Tipoca/scmutils/blob/master/src/kernel/quaternion.scm#L27

there's a JS impl here by the same folks we depend on for our Complex implementation: https://github.com/infusion/Quaternion.js/

And an older, fast Clojure implementation from @weavejester that looks well done, and well-type-hinted: https://github.com/weavejester/euclidean/blob/master/src/euclidean/math/quaternion.clj

Honestly it would be great (as you've suggested @littleredcomputer ) to take the @weavejester-style approach with Complex numbers too. If we benchmark it I bet it'd be as fast or faster than the current Complex.js approach.

A major advantage is that we could do gaussian integers.

∂ is not a valid Javascript character, and won't work for cljs

@littleredcomputer , I've hit the first (and maybe our only?) serious API problem. ∂ isn't valid, so we can't use it as a function name if we want to emit both Clojure and Clojurescript.

The only clear solution here is to rewrite everything as partial - all the tests, and the symbol tht gets emitted when we simplify an expression - but keep the (def ∂ partial) alias, so folks can still use use as a function name on the JVM.

We could also use a lowercase delta, which looks CLOSE... but, I guess, why use a tough-to-type character if it's not the right one?

@littleredcomputer , let me know what you think here. ∂ looks great, and of course we can still emit it when we generate TeX, infix and text descriptions, but we may have to let it go for quoted lists.

The benefit is that the data structure simplify emits stays compatible with scmutils.

Implement g/gcd for complex numbers

This gives us an implementation clue. We should be able to do this with an algorithm quite similar to Euclid's. This could be obvious, noting it here for later. http://mathforum.org/library/drmath/view/67068.html

Here it is from numeric.scm in scmutils, for "exact" complex numbers, ie, gaussian integers (the only thing we SHOULD be supporting!)

We could install this now, with a precondition check that (ish? z (round-complex z)).

;;; Euclid's algorithm for Exact Complex Numbers
;;;   suggested by William Throwe, to allow simplification
;;;   of rational functions with complex coefficients.

(define (round-complex z)
  (make-rectangular (round (real-part z))
		    (round (imag-part z))))

(define (gcd-complex a b)
  (cond ((zero? a) b)
        ((zero? b) a)
        (else
         (let ((q (round-complex (/ a b))))
           (gcd-complex b (- a (* q b)))))))

Port sicmutils to Clojurescript

@littleredcomputer , I'm going to use this as a staging area to record our progress, and maybe to ask questions, until we find a more efficient format.

Tasks

  • port number and complex
  • numsymb
  • add trig functions for js/BigInt, goog.math.{Long, Integer}. We may need to pull in another implementation here.
  • add ratio support via Fraction.js (#84)
  • add BigDecimal support via Decimal.js

Presentation

  • sicmutils.env
  • sicmutils.infix (#74)

Examples

  • sicmutils.examples.central-potential
  • sicmutils.examples.double-pendulum
  • sicmutils.examples.driven-pendulum
  • sicmutils.examples.pendulum
  • sicmutils.examples.rigid-rotation
  • sicmutils.examples.top

Data Structures and Existing Types

  • sicmutils.structure (#48)
  • sicmutils.matrix (#49)
  • sicmutils.polynomial (#55)
  • sicmutils.polynomial-gcd (#57)
  • sicmutils.polynomial-factor (#60)
  • sicmutils.rational-function (#62)
  • sicmutils.series (#64)
  • sicmutils.function(#66)
  • sicmutils.operator (#66)

Analysis

  • sicmutils.analyze (#55)
  • sicmutils.simplify (#63)

Calculus

  • sicmutils.calculus.basis (#50)
  • sicmutils.calculus.coordinate (#72)
  • sicmutils.calculus.covariant (#73)
  • sicmutils.calculus.derivative (#66)
  • sicmutils.calculus.form-field (#72)
  • sicmutils.calculus.manifold (#70)
  • sicmutils.calculus.map(#73)
  • sicmutils.calculus.vector-field (#72)

Numerical

  • sicmutils.numerical.compile (#68)
  • sicmutils.numerical.integrate (#77)
  • sicmutils.numerical.minimize (#77)
  • sicmutils.numerical.ode (#77)

Mechanics

  • sicmutils.mechanics.hamilton (#79)
  • sicmutils.mechanics.lagrange (#78)
  • sicmutils.mechanics.rigid (#79)
  • sicmutils.mechanics.rotation(#69)

Outstanding Issues

These questions came up during the conversion.

Misc

  • in sicmutils.numerical.compile-test, two tests are deactivated because eval doesn't work out of the box in cljs. that requires self-hosted mode, which we'll have to configure and re-enable the tests.

Numerics

  • The g/div implementations in number catch, for example, two goog.math.Longs divided, since g/div is defined between ::v/number instances. This is wrong; they should probably error. Do we want to explicitly catch and error? Or add a new type, ::inexact-number... probably the former.
  • in numbers.cljc, we have (derive ::integral ::v/number). This is probably NOT right; we should have (derive ::integral ::x/numerical-expression). This causes issues with the number? sprinkled all around. The former is bad because it causes us to try and divide goog.math.Long using /, which truncates.

Structure

  • figure out how to extend the arity by implementing some sort of variadic form

Polynomial

  • Polynomial currently calls methods from sicmutils.numsymb like sym/add, etc directly. I think we can have the polynomial methods call the fully generic g/add etc if we're more careful with our types. This would let us move some of the optimizations around numsymb without breaking code. #47 ran into trouble because of this, for example.

Matrix

The matrix component is itching to be more general. A project like https://github.com/originrose/aljabr would give us a cheap multidimensional array implementation in clj/cljs.

We could rebuild the matrix namespace on core.matrix, and implement the generics in terms of core.matrix operations.

convert sicmutils build to deps.edn

This will make it easier to consume

At first, we can have a parallel deps.edn instead of fully porting the Leiningen project, but it probably makes sense to pin on one build system eventually.

For this first task we want:

  • A working deps.edn that allows users to pull in sicmutils via a git dependency
  • notes for how to run a repl
  • how to trigger tests without leiningen

Problem when trying to def something to the value of a derivative (bit weird)

[net.littleredcomputer.math.env] > (def cs0 (fn[x] (sin (cos x))))
cs0
[net.littleredcomputer.math.env] > (def cs (compose sin cos))
cs
[net.littleredcomputer.math.env] > ((D cs) 'x)
(* -1 (cos (cos x)) (sin x))
[net.littleredcomputer.math.env] > ((D cs0) 'x)
(* -1 (cos (cos x)) (sin x))
[net.littleredcomputer.math.env] > (def yy (D cs))
CompilerException java.lang.AbstractMethodError, compiling:(form-init8768341289722602545.clj:1:9) 

Bizarrely,

[net.littleredcomputer.math.env] > (defn wwww [z] ((D sin) z)))
wwww
[net.littleredcomputer.math.env] > (wwww 'x)
(cos x)

But

[net.littleredcomputer.math.env] > (defn odear [z] ((D (compose sin cos) z)))
odear
[net.littleredcomputer.math.env] > (odear 1)
AbstractMethodError   net.littleredcomputer.math.env/odear (form-init8768341289722602545.clj:1)

Making clojure.spec kick in for sicmutils.numsymb/mul

I instrumented the mul function, but clojure.spec did not throw a provoked exception. In a nutshell this can be reproduced in any Clojure 1.9.0 repl:

(do
(require '[clojure.spec.alpha :as s])
(require '[clojure.spec.test.alpha :as ts])
(defn foo [bar] bar)
(s/fdef foo :args (s/cat :baz number?))
(defmulti foo-multi identity)
(defn make-method-fn [multi-fct fct]
 (defmethod multi-fct :no-exception [bar] (fct bar)))
(defmacro make-method-mcr [multi-fct fct]
  `(defmethod ~multi-fct :exception [x#] (~fct x#)))
(make-method-fn foo-multi foo)
(make-method-mcr foo-multi foo)
(ts/instrument)
#_(foo-multi :exception) ;spec exception as expected
(foo-multi :no-exception)) ;!!NOT EXPECTED!! no exception

The last expression does not throw the expected exception. However, the butlast would do so.
So, if I replace the sicmutils.numsymb/define-binary-operation function with

(defmacro ^:private define-binary-operation
  [generic-operation symbolic-operation]
  `(defmethod ~generic-operation [::x/numerical-expression
                                  ::x/numerical-expression]
     [a# b#]
     (make-numsymb-expression ~symbolic-operation [a# b#])))

clojure.spec kicks in. "lein test" still works after this replacement on my machine.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.