6
$\begingroup$

I would like to use a Spline-Function to describe a transformation from one range of reals to another, that is I would like to have a function which maps a value $y$ on $x$ and for simplicity let us assume that $x,y \in [0,1]$.

The idea was to define a smooth, nonlinear function which is to be within the boundary points $\{(0,0),(\alpha,\beta),(1,1)\}$ with $\alpha,\beta \in [0,1]$, also. Thus a Bezier- or BSpline-Function seemed to be appropriate.

Unfortunately I can't see how I can use the Spline-Function directly to map $y$ on $x$ (or vice versa for that). The Spline-Function will one parameter $t$ with $t \in [0,1]$ and will give all points on the curve, e.g. $spline(0) = (0,0), spline(1) = (1,1)$.

So to find say the corresponding $x$ value for $y=0.6$, I tried something like this:

f = BSplineFunction[{{0, 0}, {0.2, 0.7}, {1, 1}}];
FindInstance[Last@f[t]==0.6,t]

But what I am getting is the false answer $\{\{t\rightarrow 0.6 \}\}$. The correct answer should have been $t = 0.5$.

So far I have also tried Reduce, Solve, NSolve, FindRoot (giving a start value for t) and have of course given the domain to be Reals. But nothing works.

Is there a way to do this without having to substitute a simpler function for the Spline-Function (e.g. using Interpolation[])?

$\endgroup$
4
  • $\begingroup$ Interpolation[ ]? $\endgroup$ May 24, 2013 at 12:40
  • $\begingroup$ Thank you, Sjoerd, I should have mentioned that solution - whiich is what I am doing now: using a grid with Interpolation[] where I have gotten the best results using InterpolationOrder->2. But that still is not perfect and still wonder why equation solving won't work here. $\endgroup$
    – gwr
    May 24, 2013 at 12:46
  • $\begingroup$ @gwr Why do you expect t -> .72 since f[.5] gives y -> .6 in this case? $\endgroup$
    – BoLe
    May 24, 2013 at 12:58
  • $\begingroup$ @BoLe Uups, you are right, I should have used Last instead of First in my post here and so you are correct. But that still does not change the principle problem with equation solving. Thanks for pointing out the error, I will edit my question. $\endgroup$
    – gwr
    May 24, 2013 at 13:05

3 Answers 3

5
$\begingroup$

Inverse of your function sampled and interpolated:

g = InverseFunction@Interpolation[f /@ Range[0, 1, .1]]

(* gives x for y = .6 *)
g[.6]
    0.35

Using FindRoot:

FindRoot[second[f[t]] == .6, {t, .5}]    
    {t -> 0.5}

f[.5]    
    {0.35, 0.6}

Delaying evaluation:

second[r_?(VectorQ[#, NumericQ] &)] := r[[2]]

More general delay:

delay[f_, r_?(VectorQ[#, NumericQ] &)] := f[r]

FindRoot[delay[First, f[t]] == .6, {t, .2}]
    {t -> 0.720759}

I can't understand why BSplineFunction generates a message (Quiet for quieting that) but the result is correct, right?

$\endgroup$
8
  • $\begingroup$ Thanks for that answer, but I was aware of this possibility already and have accordingly changed my question. Why does "regular" equation solving with the Spline Function not work? $\endgroup$
    – gwr
    May 24, 2013 at 12:51
  • $\begingroup$ FindRoot can solve this. $\endgroup$
    – BoLe
    May 24, 2013 at 12:53
  • 1
    $\begingroup$ First, Last, I don't know which is x and which y in this case. :) Anyway, delayed FindRoot works. You may want to Quiet it here though. $\endgroup$
    – BoLe
    May 24, 2013 at 13:01
  • 1
    $\begingroup$ It's a special delay. It waits until f[t] evaluates to a list of numbers. (I learnt this here.) $\endgroup$
    – BoLe
    May 24, 2013 at 13:16
  • 1
    $\begingroup$ I just tested the FindRoot-Solution vs. using InverseFunctionon an interpolated Function as suggested in the solution on 10000 random values. It surprised me quite a bit to find the FindRoot-solution turning out faster (4.7 secs vs. 6.9 secs). $\endgroup$
    – gwr
    May 24, 2013 at 15:11
3
$\begingroup$

If you absolutely must use B-splines, you can explicitly build the component functions that make up the B-spline, using the usual definitions:

pts = {{0, 0}, {0.2, 0.7}, {1, 1}};

n = Min[Length[pts] - 1, 3]; (* B-spline degree *)
m = Length[pts];
(* clamped uniform knots for B-spline *)
knots = {ConstantArray[0, n + 1], Range[m - n - 1]/(m - n),
         ConstantArray[1, n + 1]} // Flatten;

{xu, yu} = Transpose[pts];
bs = BSplineFunction[pts];

(* B-spline component functions *)

f[t_] = xu.Table[BSplineBasis[{n, knots}, i - 1, t], {i, Length[pts]}];
g[t_] = yu.Table[BSplineBasis[{n, knots}, i - 1, t], {i, Length[pts]}];

Now, you can do this:

tval = t /. First @ FindRoot[g[t] == 0.6, {t, 0.6, 0, 1}]
   0.5

bs[tval]
   {0.35, 0.6}
$\endgroup$
7
  • $\begingroup$ Great, thanks. You said "if you absolutely must use B-splines" -- well I need to 'calibrate' a transformation where I do not know the mapping (function) to use. From some reasonable assumptions (all values within $[0,\infty($, the function probably not beeing s-shaped etc., I found a spline to be the best way to define a smooth function with just two parameters (next to one for scaling the maximum value). What else to use/do? $\endgroup$
    – gwr
    May 24, 2013 at 13:18
  • $\begingroup$ I would, as Sjoerd and BoLe suggested, just have used Interpolation[] to begin with. $\endgroup$ May 24, 2013 at 13:20
  • $\begingroup$ My problem is, that besides the point $(\alpha,\beta)$ where both parameters are to be estimated, and the points $(0,0)$ and $(x_\text{max},y_\text{max})$ I do not know anything else. Interpolation[] will give a function passing through the points. I find it very hard to keep it within plausible ranges? $\endgroup$
    – gwr
    May 24, 2013 at 13:27
  • $\begingroup$ Interesting... I'll have to think about it. $\endgroup$ May 24, 2013 at 13:38
  • $\begingroup$ Another approach you might want to consider would be to use (say) NonlinearModelFit -- this way you get to control very precisely the form of the function and you can choose it to have a nie shape. As an added bonus, you can perhaps even find an analytical inverse, which greatly simplifies that part of the problem. $\endgroup$
    – bill s
    May 24, 2013 at 13:44
3
$\begingroup$

Given the discussion, another approach you might want to consider would be to explicitly parameterize a function -- -- this way you get to control very precisely the form of the function and you can choose it to have a nice shape. As an added bonus, you can perhaps even find an analytical inverse, which greatly simplifies that part of the problem. For example, you might do something like:

Clear[f,g]
f = NonlinearModelFit[{{0, 0}, {0.2`, 0.7`}, {1, 1}}, b x + a x^(1/3), {a, b}, x]

You can plot this the normal way

g[x_] := Normal[f]; Plot[g[x], {x, 0, 1}, PlotRange -> All]

And of course the inverse can then be found (if the function is sufficiently simple, as this one is) using

Solve[y == g[x], x]

Probably you can think of a better functional form for your data.

$\endgroup$
2
  • $\begingroup$ That is a reasonable suggestion but since I am a bit "clueless" about which functional form to use - and what the implication of it would be - I thought "that is exactly what splines were invented for"? $\endgroup$
    – gwr
    May 24, 2013 at 14:22
  • $\begingroup$ Well, splines are just one particular polynomial/functional form. They certainly are convenient, and fast, but you only have 3 points, so simplicity and speed are not all that important. Plus getting a closed form answer for the inverse would seem to me to be a significant improvement. $\endgroup$
    – bill s
    May 24, 2013 at 14:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.