# Don't understand an evaluation with BezierFunction

I don't understand the source of the failure of the following attempts to solve an equation involving the following very simple BezierFunction.

f = BezierFunction[{{0, 0}, {0, 0.8}, {1, 1}}];


The following three approaches fail, but in what I find to be a very odd way: the returned solution is 0.5, with no warning.

Solve[0.5 == Last[f[x]], x]
NSolve[0.5 == Last[f[x]], x]
Reduce[0.5 == Last[f[x]], x]


The following approach is successful (but produces a warning):

FindRoot[0.5 == f[x][], {x, 0.5}]


To see what I find mysterious, consider the following trace.

Trace@NSolve[0.5 == Last[f[x]], x]


In particular, I would like to understand why Last[BezierFunction[{{0, 0}, {0, 0.8}, {1, 1}}][x]] evaluates to x within this trace. This is certainly not the behavior we get from ordinary functions (thank goodness):

h = t |-> {t + 1, t + 2};
Last[h[x]]  (* x+2 *)


Instead, it behaves like an undefined symbol:

Clear[h]
Last[h[x]]  (* x *)


In Mathematica, everything is an expression. When you use First or Last (which are just shortcuts to Part), you extract a part of your expression. For example:

Clear[g, x, y, z];

g[x] // Last
(* x *)

Part[g[x], -1]
(* x *)

g[x, y, z] // First
(* x *)

g[x, y, z] // Last
(* z *)


In your case, you have the following expression:

expr = BezierFunction[{{0, 0}, {0, 0.8}, {1, 1}}][x];
expr // Last
(* x *)


Is there something weird going on? No. You wanted to get the last part of your expression. Why does it look different if you use an "ordinary" function?

Last[(t |-> {t + 1, t + 2})]
(* 5 *)


It is because this function will get evaluated when you give it a symbolic argument x! But the BezierFunction will stay unevaluated when you give it a symbolic argument. It will evaluate only if you give it a numerical argument.

BezierFunction[{{0, 0}, {0, 0.8}, {1, 1}}][x]
(* BezierFunction[{{0, 0}, {0, 0.8}, {1, 1}}][x] *)

BezierFunction[{{0, 0}, {0, 0.8}, {1, 1}}][.5]
(* {0.25, 0.65} *)

BezierFunction[{{0, 0}, {0, 0.8}, {1, 1}}][.5] // Last
(* 0.65 *)


If you want to use it in Solve, you have to prevent Last acting on the expression too soon (that is: before the numerical argument is passed to the function). For example, you can do the following:

bf = BezierFunction[{{0, 0}, {0, 0.8}, {1, 1}}];
f[x_?NumericQ] := Last[bf[x]]

Solve[0.5 == f[x], x]
(* {{x -> 0.361508}} *)

• That very last bit is helpful, but I am left with three questions. i. Is it documented that it will not evaluate a symbolic argument? ii. Why not evaluate symbolically e.g. to polynomials? iii. How does this help me understand why FindRoot[0.5 == Last[f[x]], {x, 0.5}] fails but FindRoot[0.5 == f[x][], {x, 0.5}] succeeds? Thanks!
– Alan
Mar 27 at 19:59
• @Alan i. AFAIK it's not documented, so are InterpolatingFunction, BSplineFunction, etc. ii. I'd say this design is convenient, because BezierFunction is mainly for numeric purpose, and nobody want to see a horrible large polynomial as the output in most cases. Also, according to the wiki, the polynomial form of Bézier curve is not numerically stable. If you do want a polynomial, check this: mathematica.stackexchange.com/q/29633/1871 iii. As documented: Last[expr] is equivalent to expr[[-1]]. Mar 28 at 2:42
• @xzczd Yes of course. But I think your point is, Last will be applied to the unevaluated expression, while in contrast the entire expression Part[f[x],2] must remain 'unevaluated' and so subsequently becomes useful. That raises the question, why doesn't NSolve[0.5 == f[x][], x] work, and there the answer is apparently: because the solution strategy tries to invert f instead of inverting x|->f[x][] (or using another strategy entirely).
– Alan
Mar 28 at 13:34
• @Alan, the error message will tell you why FindRoot[0.5 == f[x][], {x, 0.5}] "works" and FindRoot[0.5 == Last[f[x]], {x, 0.5}] doesn't. There is no second part of f[x], and it will therefore stay in this "unevaluated" form f[x][]. However, there is the last part of f[x], so it will immediately evaluate to x before solving. Mar 28 at 13:40
• @Domen Yes, see my previous comment.
– Alan
Mar 28 at 16:22
f = BezierFunction[{{0, 0}, {0, 0.8}, {1, 1}}];
FindRoot[0.5 == Indexed[f[x], 2], {x, 0.5}]


{x -> 0.361508}

Or

FindRoot[0.5 == Last[f[x]], {x, 0.5}, Evaluated -> False]


{x -> 0.361508}

• FindRoot was already working, but this nicely gets rid of the warning. I especially like the clever use of Indexed. But is there a way to make say NSolve work?
– Alan
Mar 28 at 13:34