I'd like to programmatically construct a function from a long expression with many duplicate terms. The objective is to programmatically create a set of compiled functions with expressions that were previously created programmatically. This is somewhat related to Stack Exchange Question: How to speed up an optimization with very long symbolic expressions?
The question may be clarified with a minimal non-working example. The example is a bit silly, but it expresses the idea.
Suppose I have a list of expressions:
exampleTerms = {(x - y)^2, x y, (x - y)^4};
exprList = Expand /@ RandomChoice[exampleTerms, 10]
(*{x^4 - 4 x^3 y + 6 x^2 y^2 - 4 x y^3 + y^4,....}*)
Here is a list of terms that I'd like to precompute for insertion into a Function, or Compile:
vars =Union[Flatten[(List @@@ exprList) /. _?IntegerQ t_ :> t]]
(*{x y, x^3 y, x^2 y^2, x y^3}*)
Here is a list of rules pointing to intermediate variables:
assigments =
Thread[vars -> (Indexed[term, #] & /@ Range[Length[vars]])]
and here is what I want as the expr in Compile:
expr = exprList /. assigments
(*{x^2 -> Indexed[term, {1}], x^4 -> Indexed[term, {2}],
x y -> Indexed[term, {3}],...}*)
local = With[{termList = Indexed[term, #] & /@ Range[Length[vars]]},
MapThread[HoldForm[#1 = #2] &, {termList, vars}]]
Here is something that looks like what I want:
tmpF = Function[{x, y},
Evaluate[Block[Evaluate[local], Evaluate[expr]]]
]
But that fails because Block objects to the local variable assignment. (Also, the evaluates seem redundant, but it doesn't work if I remove them) It also fails if I don't use Indexed:
tmpF = Function[{x, y},
Evaluate[
Block[Evaluate[local /. Indexed[term, i_] :> term[i]],
Evaluate[expr]]]
]
Or, which seems to be to be a total kludge by creating my own symbol names:
localAlt = With[{termList =
ToExpression["term" <> ToString[#]] & /@ Range[Length[vars]]},
MapThread[HoldForm[#1 = #2] &, {termList, vars}]]
exprAlt =
expr /. Indexed[p_, i_] :> ToExpression[ToString[p] <> ToString[i]]
tmpF = Function[{x, y},
Evaluate[Block[localAlt, Evaluate[exprAlt]]]
]
Block
examples don't work is becauseEvaluate
will fully evaluate its arguments, including the assignments. This is course is not what you want forBlock
. What I usually do is to wrap my expressions inHold
, and then use a series of replacement to build uf the full expression, all inside theHold
wrapper. You have to make sure you are only relying on structural transformations, since your arguments (toBlock
in this case) should never be actually evaluated. $\endgroup$Hold[ exampleTerms = {(x - y)^2, x y, (x - y)^4}; exprList = Expand /@ RandomChoice[exampleTerms, 10]; vars = Union[Flatten[(List @@@ exprList) /. _?IntegerQ t_ :> t]]; assigments = Thread[vars -> (Indexed[term, #] & /@ Range[Length[vars]])]; expr = exprList /. assigments; tmpF = Function[{x, y}, Evaluate[Block[Evaluate[local], Evaluate[expr]]]] ]; ReleaseHold[%]
$\endgroup$