How to work symbolically with a compiled function?

I have a compiled function f to be used as a piece of a much larger calculation. I would like to expand out a function that calls f explicitly before evaluating the expanded version numerically. For this reason, I would like to manipulate the compiled function symbolically when inserting symbolic arguments.

As an example, if we define

f = Compile[{x}, 1 + Cos[x], CompilationTarget -> "C"]


I would like to be able to work with f[x] as an object. E.g., I would like to see

Expand[(1 + f[x])^2]


produce 1 + 2 f[x] + f[x]^2. However, when I actually run the above code, I receive the error

CompiledFunction::cfsa: Argument x at position 1 should be a machine-size real number.


and the actual output is 4 + 4 Cos[x] + Cos[x]^2 as if f[x] were evaluated symbolically. Is there a way to compile a function such that it can be manipulated symbolically when symbolic arguments are provided, but numerically when numeric arguments are provided?

• I think this may be an XY problem. Can you, please, clarify a bit more what your use case is that requires such a non trivial task to be accomplished? It is not clear even from your explanation in what use case that one would benefit from compiling a function in such a way, if this were indeed possible (as you’ve no doubt found, it is likely not, as any compiled function will really only be working on machine numbers). You might also look into memoization to increase the speed of subsequent uses of your functions, as it sounds like this may assist you in accomplishing your goals. Jun 19 at 1:54
• Try adding the option RuntimeOptions -> {"EvaluateSymbolically" -> False}. See to docs for Compile and for RuntimeOptions for more. Jun 19 at 4:28

EDIT: oooor, instead of following this answer, simply add the option RuntimeOptions -> {"EvaluateSymbolically" -> False}, as per @Michael E2's comment above :) (even though it's simple, I hope they'll post it as a separate answer!)

Here's one simple "manual" strategy: you can define an "internal" compiled function to be used when the argument is numeric:

ClearAll[f];
fcompiled = Compile[{x}, 1 + Cos[x], CompilationTarget -> "C"];
f[x_?NumericQ] := fcompiled[x]

(* Tests: *)
Expand[(1 + f[x])^2]
(* Out: 1 + 2 f[x] + f[x]^2 *)

Expand[(1 + f[x])^2] /. x -> 3
(* Out: 1.020115157 *)


Alternatively, you can be a bit more precise and prevent evaluation on complexes as well, which will still throw an error (unless you use Compile[{{x, _Complex}}, ...]):

ClearAll[f];
f[x_] := fcompiled[x] /; Element[x, Reals]


However, I'm not totally sure this is equivalent to the check that CompiledFunction performs; it might not, for instance, be impervious to assumptions on an argument x.

So just to be completely sure, we could also explicitly check that a simple compiled function doesn't generate that error message by testing one directly:

With[{c = Compile[{x}, True], m := CompiledFunction::cfsa},
CRealQ[x_] := Quiet[Check[c[x], False, {m}], {m}]
]

ClearAll[f];
f[x_?CRealQ] := fcompiled[x]


Another option:

fcompiled = Quiet[Compile[{x}, 1 + Cos[x], CompilationTarget -> "C"]]
f[x_?NumericQ] := If[x \[Element] Reals, fcompiled[x], HoldForm[f[x]]]


Tests:

(* Test 1: *)
f[I]
(*Out: f[I]*)
(* Test 2: *)
Expand[(1 + f[x])^2]
(* Out: 1 + 2 f[x] + f[x]^2 *)
(* Test 3: *)
Expand[(1 + f[3])^2]
(* Out: 1.020115157 *)