mentat-collective / emmy-viewers

High-performance symbolic, 2D and 3D visual extensions to the Emmy computer algebra system.
https://emmy-viewers.mentat.org
MIT License
39 stars 7 forks source link

Can (ev/with-params {...} func) accept *functions* as parameters? #76

Closed alexgian closed 4 months ago

alexgian commented 4 months ago

ev/with-params seems unable to process parameters that are actually functions, rather than just fixed values. I appreciate that this is due to the serialization that clerk has to perform, but is there a workaround?

One example where this becomes relevant when you need to plot a number of points with an emmy-viewer in a way that is determined by a function that might only be specified at run time.

sritchie commented 4 months ago

Can you show what you're trying to do?

alexgian commented 4 months ago

I am using mafs trying to plot a slope-field as a background to a future ODE solver demo. The functions that will determine the gradient at each point will be supplied by the user, so they are not known beforehand. I tried to pass them using ev/with-params but this was not accepted.

(def fx1 (fn [x y] 1))
(def fx2 (fn [x y] (* 2 x)))

 (ev/with-let [!fns [fx1 fx2]]
             (mafs/mafs
               {:zoom {:min 1 :max 5}}
               (mafs/cartesian {:subdivisions 5})
               (mafs/vector-field
                 {:step 0.2
                  :xy   (ev/with-params {:atom !fns :params [0 1]}
                                        (fn [af1 af2]
                                          (fn [[x y]]
                                            (let [x1 (af1 x y) x2 (af2 x y)
                                            ;     (let [x1 1 x2 (* 2 x)
                                                  h (sqrt (+ (square x1) (square x2)))]
                                              [(/ x1 h)  (/ x2 h)]
                                              ))))
                  :xy-opacity (constantly 0.4)})))
2024-05-26T22:56:41.197Z alex-HP-2570p INFO [emmy.expression.compile:661] - compiled function in 796.963 μs
Clerk evaluated 'src/demo.clj' in 257.761635ms.
Execution error (ExceptionInfo) at taoensso.nippy/throw-unfreezable (nippy.clj:1005).
Unfreezable type: class demo$fx1
alexgian commented 4 months ago

OK, I worked it out, I was overcomplicating it my mind I didn't have to use 'ev/with-params', as there are no params, only the x and y, which are supplied by the system through the :xy key.

So the function can be defined outside the mafs/vector-field call! This is all I had to do in the end

(defn fx [[x y]]
  (let [x1 1  x2 (* 2 x)
        h (sqrt (+ (square x1) (square x2)))]
    [(/ x1 h) (/ x2 h)]))

(mafs/mafs
  {:zoom {:min 1 :max 5}}
  (mafs/cartesian {:subdivisions 5})
  (mafs/vector-field
    {:step 0.2
     :xy   fx
     :xy-opacity (constantly 0.4)
     }))

Issue can be closed now.