scicloj / wolframite

An interface between Clojure and Wolfram Language (the language of Mathematica)
Mozilla Public License 2.0
55 stars 2 forks source link

Support negative number representing symbols such as `-E4` #42

Open light-matters opened 6 months ago

light-matters commented 6 months ago

Would it be worth making it easier to pass negative symbols, e.g. '-E4 instead of '(- 0 E4).

At the moment, this will be interpreted as a non-alphanumeric symbol.

light-matters commented 6 months ago

Something like this seems to work (outputs {-y "Minus[y]", -x "Minus[x]"}) :

  (def expression '(+ -x -x -y -5 -2 (- x 10 5) (** x 2)))

  (defn strip-ns [form]
    (walk/postwalk (fn [form]
                     (if (qualified-symbol? form)
                       (symbol (name form))
                       form))
                   form))

   (defn replacement-map
    "Creates a replacement map for negative symbols, such that they are reasonably interpreted by Wolfram.

  TODO:
  - Extend the idea to deal with other custom replacements (e.g. greek/hebrew symbols.) .
  "
    [expression]
    (let [syms (->> expression
                    strip-ns
                    (into '())
                    (tree-seq list? seq)
                    (remove (some-fn list? nil?))
                    (filter #(when (symbol? %)
                               (->> %
                                    str
                                    (re-matches #"-.+"))))
                    distinct)

          syms-base (map (fn [sym] (-> sym str char-array rest (#(apply str %)) symbol)) syms)]

      (zipmap syms (map (fn [sym] (format "Minus[%s]" sym)) syms-base))))

  (replacement-map expression)
  (replacement-map `(+ -x -x -y -5 -2 (- x 10 5) (** x 2)))

Last two s-exps show that it works for quoted and backticked expressions.

light-matters commented 6 months ago

Would it make more sense to actually do the replacement during the expression walk? This would require a fairly fundamental change to how wolframite usually works, but it would be powerful to have user-specified replacement functions.

At the moment, we can pass the generated replacement map to ->wl and it will get passed down the line, e.g.

(->wl '(+ -x -x -y -5 -2 (- x 10 5) (** x 2))
      {:aliases {'-x '(Minus x)
                   '-y '(Minus y)
                   '** 'Power}})

produces

#object[com.wolfram.jlink.Expr 0x23a65a14 "Plus[Minus[x], Minus[x], Minus[y], -5, -2, Subtract[x, 10, 5], Power[x, 2]]"]
holyjak commented 4 months ago

Something like this seems to work ...

@light-matters Not sure these comments have anything to do with supporting negative symbols, as in -E4??

holyjak commented 4 months ago

This could likely be implemented easily by extending wolframite.base.convert/convert impl. for :symbol, where we currently throw if the symbol does not match #"[^a-zA-Z0-9$\/]" - we could check that it is alphanum. and starts with - and replace that with Minus[...].

But this is not something Wolfram itself supports so IMO this is beyond v1. Notice that after #58 we will support (w/- E4).

Hm, what is E4 anyway? It does not seem Wolfram knows this? Is this meant for fn args, as in (w/Map (w/fn [x] -x) [1 2]) ??

light-matters commented 4 months ago

Something like this seems to work ...

@light-matters Not sure these comments have anything to do with supporting negative symbols, as in -E4??

-E4 is symbolically equivalent to -x,-y etc. unless I misunderstand what you're meaning. I suppose it might have made more sense to use E4 as an example.

light-matters commented 4 months ago

This could likely be implemented easily by extending wolframite.base.convert/convert impl. for :symbol, where we currently throw if the symbol does not match #"[^a-zA-Z0-9$\/]" - we could check that it is alphanum. and starts with - and replace that with Minus[...].

But this is not something Wolfram itself supports so IMO this is beyond v1. Notice that after #58 we will support (w/- E4).

Hm, what is E4 anyway? It does not seem Wolfram knows this? Is this meant for fn args, as in (w/Map (w/fn [x] -x) [1 2]) ??

Wolfram does support this, as I can evaluate "-x + 10" in Mathematica, but beyond v1 sounds right for us. As you say, (w/- E4) mostly solves the issue for now (it is better than having to write "(- 0 symbol)" just to make 'symbol' negative.

(wl/eval (w/- 'E4))
(wl/eval '(- E4))

both work as expected.

E4 is just an arbitrary symbol, e.g. 'equation4' or 'e' or 'x'. This was probaly a bad example, but just happened to be the thing I was working with when I reported the issue!