clojure-numerics / expresso

Clojure library for symbolic computation
312 stars 20 forks source link

Having trouble building a rule #32

Open Vaguery opened 5 years ago

Vaguery commented 5 years ago

The use case is relatively simple:

The arithmetic expressions I'm working with have a k symbol in them (an "ephemeral random constant"), which is interpreted as "some number, which is almost certainly different in each case".

So for example, in a simple-sounding scheme where k just appears like a variable name in an expression, I would want to override expresso's simplification dynamics so that (/ k k) does not get simplified to 1.0, but instead is reduced to k. The same applies to almost any arithmetic function; (+ k k) :=> k, for example, and so on.

(ns hashed-symbolic-regression.core-test
  (:require 
            [numeric.expresso.core :as ex]
            [numeric.expresso.rules :as er]
            ))

(declare k) ;; ERC function

(defn k? [item] (= k item))

(prn (k? k)) ;; true

(def erc-mult (er/rule (ex/ex (* ?x ?y)) :=> k
  :if (er/guard (and (k? ?x) (k? ?y)))
  ))

(def two (ex/ex (* k k)))

(prn (er/apply-rule erc-mult two))

I've tried an awful lot of different variations on the rule definition of erc-mult, and none of them seem to recognize a pattern like (+ k k), regardless of number of matchers, serial matchers, quotes, all kinds of stuff. The same kind of pattern will definitely recognize (+ 88 88) if I define it to handle an explicit number, it just won't manage k as far as I can tell.

Is there something I've missed in defining a rule on an abstract symbol like this? I feel as though I've been over all the docs in detail, but I just can't seem to get through this blockage.

Vaguery commented 5 years ago

Update: If I use the keyword :k there's no problem getting my rules to fire, but there are difficulties afterward.

Is the trick here that I should not be using a var for my "special variable"?

My goal here is to permit a simplification of something like (* (+ x x) (+ k k)) to (eventually) (* k x), using rewrite rules something like:

(+ x x) -> (* 2 x)
(+ k k) -> k
(* (* 2 x) k) -> (* 2 x k) -> (* k x)

(and so on).

When I use a keyword, my specially-defined rules for :k work under transform-expression, but the standard arithmetic simplification fails on the keyword. When I use a symbol k, the standard arithmetic simplification rules are applied to the non-standard symbol k as if it were a variable name.

Any advice would be welcomed.

Vaguery commented 5 years ago

As I try things, a few more (of my) mistakes become apparent and I seem to be better able to say what I'm looking for:

What I want to be able to do is (1) standard arithmetic simplification, but (2) using a special set of domain-specific additional rules for a particular symbol, keyword, or function.

When I use a symbol for the special thing ('k), the standard arithmetic simplification rules do things I don't want to have happen, like (/ k k) :=> 1.0. That is not true, for this definition of 'k as a late-binding random variable.

When I use a keyword for the special thing (:k), the standard arithmetic simplification rules die when I apply them to anything like (+ x :k).

When I use a function for the special thing, like this:

(declare erc)

(defn k [] (erc))

(def k-mul (rule (ex (* (k) (k) ?&*)) :=> '(* (k) ?&*)))

The code doesn't compile.

The closest I seem to be able to come, so far, is to copy all of the algebraic simplification rules from the core library, and add a guard clause like (not= ?x k) as much as I can....