8000 warm diggety: a first crack at geometric algebra ('hestenic.cljc') by vanderoops · Pull Request #347 · sicmutils/sicmutils · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

warm diggety: a first crack at geometric algebra ('hestenic.cljc') #347

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 14 commits into
base: main
Choose a base branch
from

Conversation

vanderoops
Copy link
@vanderoops vanderoops commented Apr 12, 2021

Here's another geometric algebra note for when we get this ported over: https://marctenbosch.com/quaternions/

@codecov-io
Copy link

Codecov Report

Merging #347 (6d79f27) into master (b953c90) will not change coverage.
The diff coverage is 100.00%.

Impacted file tree graph

@@           Coverage Diff           @@
##           master     #347   +/-   ##
=======================================
  Coverage   83.04%   83.04%           
=======================================
  Files          92       92           
  Lines       10932    10932           
  Branches      526      526           
=======================================
  Hits         9078     9078           
  Misses       1328     1328           
  Partials      526      526           
Impacted Files Coverage Δ
src/sicmutils/env.cljc 92.04% <100.00%> (ø)
src/sicmutils/polynomial.cljc 88.70% <0.00%> (-0.67%) ⬇️
src/sicmutils/numerical/elliptic.cljc 89.76% <0.00%> (+0.68%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update b953c90...6d79f27. Read the comment docs.

@@ -47,6 +47,7 @@
[sicmutils.abstract.function :as af #?@(:cljs [:include-macros true])]
[sicmutils.abstract.number :as an]
[sicmutils.complex]
;; [sicmutils.hestenic :as h] ;; well, this sure doesn't work...
Copy link
Member

Choose a reason for hiding this comment

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

Interesting if the file compiles... What was the error? I'll give it a test too.

;;
;;

(ns sicmutils.hestenic
Copy link
Member

Choose a reason for hiding this comment

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

first pass, I'll just make notes about the code, not high level design, as I go through! Design next.

;;
;; note that we extend many of these protocols too to java.lang.Number
;; to enjoy automatic interop of existing numeric types with the GA
;; system. thanks, lisp dialect!
Copy link
Member

Choose a reason for hiding this comment

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

excellent! We can beef this up to get it working for the various cljs types too.

;;

(defprotocol IHestenic
(scl [this s])
Copy link
Member

Choose a reason for hiding this comment

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

Stingy with your character count :) For the final push, you can also put docstrings on protocol methods, right after the arg vector.

Copy link
Member

Choose a reason for hiding this comment

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

It might be worth separating out the functions that you think are going to be covered by the existing generics - neg, prd, inv, quo, dot, eq?, scl, sum.

We have a wedge product in the differential geometry stuff, so we can make that a generic and have the same function do the right thing for differential forms and GA.

(grade-part [this n]))

(defprotocol IGradable
(grade [this]))
Copy link
Member

Choose a reason for hiding this comment

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

same note here about docstrings!



;;
;; A Gradeling is a bevy of Bladoids of the same grade. It may or may not be
Copy link
Member

Choose a reason for hiding this comment

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

what's a bevy of bladoids?

Copy link
Author

Choose a reason for hiding this comment

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

(as discussed yesterday)

(defn gradeling [grade-or-bladoids]
(if
(integer? grade-or-bladoids)
(Gradeling. grade-or-bladoids ())
Copy link
Member

Choose a reason for hiding this comment

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

I think you depend on these being vectors in a few spots, so prefer [].

Copy link
Author
@vanderoops vanderoops Apr 15, 2021

Choose a reason for hiding this comment

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

excellent. so let the record show!

IGradeling
(bladoids [_] bldds)
(bladoid-with-basis [this b]
(first (filter (fn [el] (= (basis el) b))
Copy link
Member

Choose a reason for hiding this comment

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

I wouldn't do it here but just for Clojure fun you could also:

(some #(when (= b (basis %)) %)
      (bladoids this))

Copy link
Author
@vanderoops vanderoops Apr 15, 2021

Choose a reason for hiding this comment

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

any reason this'd be preferred? (some's vague and indeterminate connotation (in english) feels like a miscue to me in this context... but then i'm easily duped.)

Object
(toString [_] (str "g" gr "{"
(reduce (fn [sofar bl]
(str sofar (if (empty? sofar) "" " ") bl))
Copy link
Member

Choose a reason for hiding this comment

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

(clojure.string/join " " bldds) for the guts.

Copy link
Author

Choose a reason for hiding this comment

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

done.


Object
(toString [_] (str "MV["
(reduce (fn [sofar gr]
Copy link
Member

Choose a reason for hiding this comment

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

(clojure.string/join " " grdlgs) for the guts

Copy link
Author

Choose a reason for hiding this comment

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

prima! toll! fabelhaft! (schon gemacht)

Copy link
Author

Choose a reason for hiding this comment

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

digging it everywhere all the time!

Copy link
Member
@sritchie sritchie left a comment

Choose a reason for hiding this comment

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

Okay, so much good stuff here!!!

A3D4
(nil? neck)
(list head)
(same-basis? head neck)
(recur (sum head neck) (second trunk) (rest trunk))
Copy link
Member

Choose a reason for hiding this comment

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

We've got it! I'll recommend

(defn- collapse [blades]
  (map (partial reduce sum)
       (partition-by basis blades)))

(defn- order-swap-count [basl basr]
(loop [cnt 0
lind (dec (count basl))]
(if (< lind 0)
Copy link
Member

Choose a reason for hiding this comment

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

I find it more readable sometimes in case checks like this to use the explicit neg?, pos? zero?...

Copy link
Author

Choose a reason for hiding this comment

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

for me, those predicates (pos? et al.) pertain meaningfully to interrogating the essence of an immutable thing; whereas here we're doing what amounts to a low-level imperative-programming loop, with temporary index variables that are changing and whose instantaneous state needs to be tested. the (< x 0) style feels consonant with that. a matter of taste, of course...

Choose a reason for hiding this comment

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

While predicates mostly seem to be implemented in Java for speed, taking pos? as a specific example, those implementations are still doing an inequality check at the end of the day, since pos? is a wrapper around a function called isPos. No prizes for guessing how that's implemented. A rose by any other name...

The important idea here is binding that procedure to a name that scans faster, clearly communicates the aglorithm being used, and has less arguments you have to worry about getting wrong. I think it only feels consonant because the expectation that looping conditions look like that has been burnt into our minds by decades of C-overexposure.

cnt
(let [lval (nth basl lind)
sc (scoot-count lval basr)]
(if (= sc 0)
Copy link
Member

Choose a reason for hiding this comment

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

zero?

Copy link
Author

Choose a reason for hiding this comment

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

keep wishing that clojure had a nonzero? predicate!

(grade [_] gr)

Object
(toString [_] (str "g" gr "{"
Copy link
Member

Choose a reason for hiding this comment

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

Tip for toString:

;; also, add an entry to src/data_readers.cljc:

{c sicmutils.complex/complex-from-pair}

;; This is a function of the data structure, or FORM, (already parsed by
;; Clojure's reader) that comes after #c.

(defn complex-from-pair [[re im]]
  (complex re im))

(defmethod print-method Complex [^Complex v ^java.io.Writer w]
  (.write w (str "#c " [(g/real-part v)
                        (g/imag-part v)])))

(defmethod print-dup Complex [^Complex v ^java.io.Writer w]
  (.write w (str "#c " [(g/real-part v)
                        (g/imag-part v)])))

Copy link
Member

Choose a reason for hiding this comment

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

Whoops, ignore the print-dup thing, you only need print-method.

(same-basis? [this otha]))


(deftype Bladoid [cf bss]
Copy link
Member

Choose a reason for hiding this comment

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

these abbreviations are a little cryptic for me... can we call them coef and basis? You can still write accessors for them with those same names.

Also, if you add an m field, you can implement metadata by implementing IObj:

;; at the top of the file
(:import (clojure.lang IObj))

;; in the typedef
IObj
(meta [_] m)
(withMeta [_ m] (Bladoid. coef basis m))

The clojurescript version will be slightly different. I can help you get this all working for cljs too!

Copy link
Author

Choose a reason for hiding this comment

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

a personal proscription is in play here. more on that via a different medium.

have adopted the following scheme: all fields' symbols' names will be the obvious ascii, prepended by f_.

so in this case we now have

(deftype Bladoid [f_coef f_basis]
  IBladoid
  (coef [_] f_coef)
  (basis [_] f_basis)
  ...

Copy link
Author

Choose a reason for hiding this comment

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

... as to the three-letter naming schema's crypticicity -- one may note that way way down at the bottom of the source code there are now explicitly semantic expansions for all the IHestenic protocol's denizens. e.g.:

(def grade-involution gri)


(defn- mv-map-biexploded-gradelings-and-sum [funq mvl mvr]
(reduce mv-absorb-mv
(MV. ())
Copy link
Member

Choose a reason for hiding this comment

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

You might consider creating and binding this empty multivector once.

Copy link
Author

Choose a reason for hiding this comment

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

sarantastic! done.

hm. looking at it in a bunch of code revealed that empty-mv wasn't as visually distinguished as (MV. [])... but problem solved by upgrading the name to the-empty-mv, which also de-ambiguifies empty's part of speech. have gone back and done zero-element --> the-zero-element as well.

and also laid in a fresh monosupply of the-empty-gradeling for good measure, and because the mood demands it.

;; rung conversions: one, yea, unto another.
;;

(extend-type Bladoid
Copy link
Member

Choose a reason for hiding this comment

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

You can do these inside the type definition too, by the way.

Copy link
Author

Choose a reason for hiding this comment

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

aye. though not all protocol implementations can be so done, because of forward reference issues.

this one'd work, i think, but i prefer it in a separate corral for cognitive-clustery goodness.

C95D

(asGradeling [this]
(Gradeling. 1 (filter (fn [bl] (not= 0 (coef bl)))
(map-indexed
(fn [ind cf] (bladoid cf (vector ind)))
Copy link
Member

Choose a reason for hiding this comment

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

Prefer the literal [ind] to (vector ind), especially if you change vect to vector above!

Copy link
Author

Choose a reason for hiding this comment

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

as vect ain't going nowheauh...

... can ye kindly explain the preference?

spankOutNothingness
(gradelings this))
spnkd (filter
(fn [grdl] (not (number? grdl)))
Copy link
Member

Choose a reason for hiding this comment

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

v/number?

Copy link
Author

Choose a reason for hiding this comment

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

ibid..dibi

426B
(quo [this otha]
(prd this (inv otha)))
(dot [this otha]
(reduce +
Copy link
Member

Choose a reason for hiding this comment

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

no need for the 0, reduce calls the 0-arity of its aggregator function if there is no initializer, and that will return 0.

Copy link
Author

Choose a reason for hiding this comment

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

sho' 'nuf.

Copy link
Author

Choose a reason for hiding this comment

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

right. i kind of liked it originally for its signalling that it's specifically a scalar getting accumulated... but have now taken it out.

Borton Fweems added 3 commits April 15, 2021 23:05
... for which: thanks abundant to sritchie! many improvements stylistic;
 a few representational. more and then more to come!
... meaning that their printed forms can now be recognized by the
reader and hoovered back in. Congruent ouput versions too for Gradeling
and MV, though as yet unclear whether there's value in reader-specialized
input for these two.
(defmacro with-metric
[tric & formses]
`(binding [*hestenic-metric* ~tric]
~(conj formses 'do)))
Copy link
Member

Choose a reason for hiding this comment

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

You shouldn't need the do, binding can handle multiple forms internally! Try ~@formses.

Copy link
Author

Choose a reason for hiding this comment

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

excellent; thanks! (and: done.)

(if (not= gra (grade head))
(throw (Exception. (str "incompatible grade: "
head " but should be " gra)))
(if (not (empty? body))
Copy link
Member

Choose a reason for hiding this comment

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

prefer (if (seq body) ...)

Copy link
Author

Choose a reason for hiding this comment

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

prithee: wherefore?

([blds gra]
(if (empty? blds)
blds
(loop [head (first blds)
Copy link
Member

Choose a reason for hiding this comment

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

The doseq is going to be clearer, I think, vs an explicit loop/recur...

Borton Fweems added 2 commits May 3, 2021 13:45
@sritchie sritchie force-pushed the main branch 3 times, most recently from afa6dd7 to c33ef42 Compare February 15, 2022 22:57
@huahaiy
Copy link
huahaiy commented Aug 14, 2022

This PR appears to be stalled. What's the holdup? Looking forward to using this to learn GA.

@vanderoops
Copy link
Author
vanderoops commented Aug 15, 2022 via email

@sritchie
Copy link
Member

@huahaiy this was a boutique exploration into GA, as @vanderoops notes; if you're interested in learning GA with sicmutils, I encourage you to go for it!

What sort of materials are you looking for? An implementation of the types, sure, but beyond that, is there a book you're following along with, or something like that? Or are you looking for essays etc that go through the concepts, in addition to code?

@huahaiy
Copy link
huahaiy commented Aug 16, 2022

@vanderoops @sritchie Thanks for responding. I am not familiar with sicmutils at this moment, and certainly would be willing to dive in more, particularly if it has a good GA implementation, as that was my main interest.

I have a few elementary books on GA, and they all mention the use of some software for some parts of the study. For example, Alan Macdonald's Linear and Geometric Algebra uses a GA module in SymPy; Miroslav Josipovic's Geometric Multiplication of Vectors uses Cl3 in Mathematica; Dorst et al.'s Geomtric algrebra for computer science discusses implementation of GA and has their own C++ implementation.

I would ideally like an implementation of GA in Clojure to play with, hence my interest in this PR.

Borton Fweems added 4 commits November 26, 2022 13:56
…c ...

... and, indeed, add both 'grade' and 'grades' methods. Also, rename
the grade selection (projection) operator from 'grade-part' to 'grp',
to underscore its fundamentalness and parity with other operators. But
then also alias the old prolix name back to the new ensveltened one.
... and also fix a shockingly naive-and-incomplete implementation of
dot product for Vect.
... in order to support standalone loading (and by 'support' we mean
'enable at all without causing an error' (and by 'we' we mean 'i' (and
by 'mean' we mean 'stony-hearted'))).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants
0