Skip to content
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

Can compare handle literal square root? #4

Open
a1exsh opened this issue Nov 28, 2022 · 10 comments
Open

Can compare handle literal square root? #4

a1exsh opened this issue Nov 28, 2022 · 10 comments

Comments

@a1exsh
Copy link

a1exsh commented Nov 28, 2022

Hi! First of all, thanks for putting your time into this project — I've found it extremely pleasant to work with so far! :-)

Now to my question. I'd like to print a table, sorted by a column where among numerical values like whole or rational numbers, occasionally a square root of a literal 2 would appear. For that to work (it doesn't currently) the generic comparison of such literals has to be supported. Is that feasible?

Currently:

(compare 2 3)
=> -1

(compare (literal-number 2) (literal-number 3))
=> -1
(compare 1 (sqrt (literal-number 2)))
Execution error (ClassCastException) at (REPL:1).
null

(compare (sqrt (literal-number 2)) (sqrt (literal-number 3)))
Execution error (ClassCastException) at (REPL:1).
null

E.g. a naïve approach could take the literal under the root and compare it to the other hand side, squared (or square both sides in case of the sqrt-to-sqrt comparison). How deeps is this rabbit hole after all? :-)

@sritchie
Copy link
Member

Good questions! I think what you're suggesting is a good idea, that we want some function that will go and evaluate out exact-but-still-numeric values in an expression.

If you want a comparator that will work between symbolic expressions, numbers, etc, give sicmutils.expression/compare a try: https://github.com/sicmutils/sicmutils/blob/main/src/sicmutils/expression.cljc#L229-L278

Also note sicmutils.value/compare, aliased as sicmutils.env/compare:

user> (clojure.core/compare 1 (literal-number 2))
Execution error (ClassCastException) at user/eval46014 (REPL:67).
class sicmutils.expression.Literal cannot be cast to class java.lang.Number (sicmutils.expression.Literal is in unnamed module of loader clojure.lang.DynamicClassLoader @1a0bb8bd; java.lang.Number is in module java.base of loader 'bootstrap')
user> (sicmutils.value/compare 1 (literal-number 2))
-1

So for now I suggest you write your own compare that tries to evaluate "exact" numbers on both sides:

(require '[pattern.rule :as r :refer [=>]])
(require '[sicmutils.value :as v])

(def exact->numeric
  (let [g (find-ns 'sicmutils.generic)]
    (r/rule-simplifier
     (r/rule (?op ??xs)
             #(every? v/number? ('??xs %))
             (? (fn [{op '?op xs '??xs}]
                  (apply (ns-resolve g op) xs)))))))

(defn my-compare [l r]
  (v/compare
   (exact->numeric l)
   (exact->numeric r)))

(my-compare 1 (literal-number (sqrt 2)))
;;=> -1

A similar idea comes up in the original scmutils library, where Sussman has an = implementation for expressions that checks if (zero? (simplify (- l r))... it's off by default because it makes = an expensive operation, but it's still a good idea to have available!

Some other notes on your code:

  • Just for fun, exprs2 can be written this way too:
(defn exprs2 [n]
  (if (= 1 n)
    [2]
    (mapcat (fn [s]
              (for [op ops
                    l  (exprs2 s)
                    r  (exprs2 (- n s))]
                (list op l r)))
            (range 1 n))))
(require '[pattern.rule :as r :refer [=>]])

(def reval
  (r/rule-simplifier
   (r/rule
    (expt ?x 1/2)     => (sqrt ?x))))

(reval '(+ x (expt y 1/2)))
;;=> (+ x (sqrt y))

Or if you also / instead want to match symbolic (/ 1 2), not just the actual value 1/2:

(def reval
  (r/rule-simplifier
   (r/ruleset
    (expt ?x 1/2)     => (sqrt ?x)
    (expt ?x (/ 1 2)) => (sqrt ?x))))

(reval '(+ x (expt y (/ 1 2))))
;=> (+ x (sqrt y))

@sritchie
Copy link
Member

@a1exsh Also, the biggest help possible for the library is publishing this stuff out and singing its praises! I'll be getting more and more visualization etc going in the next month or two, but it was great to see your clerk notebook with its symbolic code. Keep track of what could be better and we'll get it done :)

@a1exsh
Copy link
Author

a1exsh commented Nov 28, 2022

Good questions! I think what you're suggesting is a good idea, that we want some function that will go and evaluate out exact-but-still-numeric values in an expression.

@sritchie thanks for the response! :)

If you want a comparator that will work between symbolic expressions, numbers, etc, give sicmutils.expression/compare a try: https://github.com/sicmutils/sicmutils/blob/main/src/sicmutils/expression.cljc#L229-L278

Also note sicmutils.value/compare, aliased as sicmutils.env/compare:

user> (clojure.core/compare 1 (literal-number 2))
Execution error (ClassCastException) at user/eval46014 (REPL:67).
class sicmutils.expression.Literal cannot be cast to class java.lang.Number (sicmutils.expression.Literal is in unnamed module of loader clojure.lang.DynamicClassLoader @1a0bb8bd; java.lang.Number is in module java.base of loader 'bootstrap')
user> (sicmutils.value/compare 1 (literal-number 2))
-1

I should have included a little more context in my examples, but I was actually referring to sicmutils.value/compare, not clojure.core/compare. ;)

The other one, sicmutils.expression/compare doesn't throw exceptions when given some literals, but behaves... funny: https://github.com/a1exsh/notes/blob/main/notebooks/literal_compare.clj

Looks like it can be useful for something, but I'm not sure exactly what's the use case (note the very last result, not sure how we end up there even with hashes)...

So for now I suggest you write your own compare that tries to evaluate "exact" numbers on both sides:

(require '[pattern.rule :as r :refer [=>]])
(require '[sicmutils.value :as v])

(def exact->numeric
  (let [g (find-ns 'sicmutils.generic)]
    (r/rule-simplifier
     (r/rule (?op ??xs)
             #(every? v/number? ('??xs %))
             (? (fn [{op '?op xs '??xs}]
                  (apply (ns-resolve g op) xs)))))))

(defn my-compare [l r]
  (v/compare
   (exact->numeric l)
   (exact->numeric r)))

(my-compare 1 (literal-number (sqrt 2)))
;;=> -1

A similar idea comes up in the original scmutils library, where Sussman has an = implementation for expressions that checks if (zero? (simplify (- l r))... it's off by default because it makes = an expensive operation, but it's still a good idea to have available!

Need to wrap my head around that first %)

--
Alex

@sritchie
Copy link
Member

The goal with that literal compare is to give SOME way to sort arguments into commutative functions like * so we can compare bigger expressions. Otherwise (* x y) won't equal (* y x), etc... but I see that it's not what you need here.

@sritchie
Copy link
Member

And yeah the matcher I wrote at the end is not obvious at all!! I think we need some better syntax for this use case, there's an issue somewhere around making this nicer. I'll comment soon.

@a1exsh
Copy link
Author

a1exsh commented Nov 29, 2022

@sritchie I have more questions about literal numbers, e.g. I see that negative? or real? predicates don't work like I expect them to. What is the best way to discuss these (and probably more questions)? Should I raise additional issues here, or you prefer email/IRC/etc?.. :)

@sritchie
Copy link
Member

I wouldn't be surprised if you've found some bugs! Let's chat in the #sicmutils channel at https://clojurians.slack.com/. I'll be on within an hour or so and hanging for most of the day.

@a1exsh
Copy link
Author

a1exsh commented Nov 29, 2022

Looks like I need an invitation to join the server?..

@sritchie
Copy link
Member

Odd, I didn't think that was the case but here's a link:

@sritchie
Copy link
Member

Join me on Slack -- it’s a faster, simpler way to work. Sign up here, from any device: https://join.slack.com/t/clojurians/shared_invite/zt-1kp1qss90-Aod_ANmRUKFtZ7S9CpBIVg

@sritchie sritchie transferred this issue from sicmutils/sicmutils Jan 24, 2023
@sritchie sritchie transferred this issue from another repository Jan 24, 2023
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

No branches or pull requests

2 participants