I'm trying to do some very basic differential geometry of space curves. For example, a space curve $\gamma:\mathbb R\to\mathbb R^3$ has unit tangent and normal vectors given by $$t(s)=\frac{\gamma'(s)}{\lVert\gamma'(s)\rVert},\\ n(s)=\frac{t'(s)}{\lVert t'(s)\rVert}.$$ I want to define functions that would compute the tangent and normal vectors of an arbitrary curve at any $s$. I tried it in a higher-order functional style, like in say Haskell:

norm[x_] := Sqrt[x.x]
tangent[γ_] := Function[s, γ'[s]/norm[γ'[s]]]
normal[γ_] := Function[s, tangent[γ]'[s]/norm[tangent[γ]'[s]]]

(I need to define my own norm because Mathematica doesn't like to differentiate Norm.)

Unfortunately, this doesn't work. Let γ[s_] := {Cos[s], Sin[s], 0} be our test curve, a circle. Then normal[γ][1] // N does not return a vector, but instead a bizarre expression involving the dot product of 1. and a vector. I have no idea how to begin figuring out what's going on.

I can do it in a less elegant way as

tangent[γ_, s_] := γ'[s]/norm[γ'[s]]
normal[γ_, s_] := D[tangent[γ, ss], ss]/norm[D[tangent[γ, ss], ss]] /. ss -> s

but this requires the use of a dummy variable and the not-so-pretty D[tangent[γ, ss], ss] construction.

Why does my original implementation not work, and is there a simple way to fix it?

  • 1
    $\begingroup$ Have you taken a look at this mathematica.stackexchange.com/questions/18598/… ? If you put for r[t] a different curve it should work as well. $\endgroup$ – Artes Mar 2 '13 at 23:19
  • $\begingroup$ @Artes: I hadn't seen that before. But I'd rather not redefine tangent and normal for every curve I might be interested in. $\endgroup$ – user484 Mar 2 '13 at 23:42

Your normal isn't working because the derivative is getting "confused" because of the way that your functions pass parameters:

multiplyBy2[r_] := Function[s, 2 r[s]]
r[s_] := {s^2, s^2}

As demonstrated by evaluating the above, the apostrophe sees that your parameter is r, so it takes the derivative with respect to r. That's how the 1 appears in your expression. Specifically, in your original functions, this 1 (derivative of the vector with respect to itself) appears where we expect a vector still, thus creating the unwanted dot products.

This can be avoided by being more explicit with what we want to derive (note simply taking the derivative with respect to s inside the function won't work as s will be replaced by its value before the derivative is taken, resulting in an error).

Thus, in my approach, I use a "dummy" variable, true, but I think the code still remains fairly concise and avoids ReplaceAll.

norm[x_] := Sqrt[x.x]
tangent[γ_] := Function[s, γ'[s]/norm[γ'[s]]]
normal[γ_] := 
  With[{deriv = Function[x, Evaluate[D[tangent[γ][x], x]]]}, 
    Function[s, deriv[s]/norm[deriv[s]]]
γ[s_] := {Cos[s], Sin[s], 0}
normal[γ][1] // N

Finally, when actually using these function, such as within a manipulate, I strongly recommend that you create an evaluated version of the tangent and normal functions for the specific function that is being looked at, thus avoiding constant and expensive re-derivation. I.e., doing something like this:

solenoid[s_] := {Cos[s], Sin[s], s}
solTan = tangent[solenoid];
solNorm = normal[solenoid];


It has come to my attention that something closer to what OP's code uses is multiplyBy2[r]'[s]. After reviewing the Trace of this expression, it is clear as to why the expression evaluates to a matrix - the ' automatically takes the derivative of the sub-expression in Function:

Function[a, x]' hi
(* hi Function[a, 0] *)

It appears that, completely independent of the parameters of the function, the ' travels to x, yielding Function[a, x'] hi (again, see the Trace). Since x is a symbol with no parameters, x' evaluates to 0.

However, in my example, something even more confusing happens: In the Trace, the following steps occur Function[s$,2 r[s$]]',Function[s$,{2 r'[s$],2 r'[s$]}], from which we can see how a matrix may result. Why this happens, I don't know. This is why we explicitly define what we are deriving with respect to.

|improve this answer|||||
  • $\begingroup$ A closer analogue would be multiplyBy2[r]'[s], but that strangely yields a matrix, which is still not what I expected. I guess I see the problem, though I'm still in the dark about why it happens. Should I just avoid using primes on higher-order functions? $\endgroup$ – user484 Mar 3 '13 at 0:27
  • $\begingroup$ @RahulNarain I took a look at the Trace of the expression in your comment, and, as I have singled out in my answer, for some reason, Mathematica jumps from my specified expression to something completely unexplainable. Yes, I would suggest only using primes on symbols that are defined on a "single level." $\endgroup$ – VF1 Mar 3 '13 at 0:46

As VF1's answer explains, the problem is indeed that the prime behaves strangely in tangent[γ]'. I replaced it with a function

d[f_] := Function[x, Evaluate[D[f[x], x]]]

based on the construction in VF1's answer, and now everything works as expected.

tangent[γ_] := Function[s, d[γ][s]/norm[d[γ][s]]]
normal[γ_] := Function[s, d[tangent[γ]][s]/norm[d[tangent[γ]][s]]]

γ1[s_] := {Cos[s], Sin[s], s} (* a helix! *)
γ2[s_] := {s, s^2, 0}
{normal[γ1][s], normal[γ2][s]} // Simplify[#, Assumptions :> s ∈ Reals] &
(* {{-Cos[s], -Sin[s], 0}, {-((2 s)/Sqrt[1 + 4 s^2]), 1/Sqrt[1 + 4 s^2], 0}} *)
|improve this answer|||||
  • $\begingroup$ Probably I should also replace every Function[s, ...] with Function[s, Evaluate[...]] for performance as VF1 suggested. Or would that have any side effects? $\endgroup$ – user484 Mar 3 '13 at 2:32

Here is a simple fix of the OP's definitions:

norm[x_] := Sqrt[x.x]
tangent[γ_] := Function[s, Evaluate[γ'[s]/norm[γ'[s]]]]
normal[γ_] := Function[s, Evaluate[tangent[γ]'[s]/norm[tangent[γ]'[s]]]]

The evaluation of the bodies will take care of the difficulties of tangent[γ]'[s]. (The evaluation of the body of normal is not necessary, unless you plan to take its derivative.) One gets for example

γ[s_] := {Cos[s], Sin[s], 0};
normal[γ][1] // N
(* -> {-0.540302, -0.841471, 0.} *)

The reason for the trouble in the original definition of tangent is that the (symbolic) derivative of γ'[s]/norm[γ'[s]] is

γ᾽᾽[s]/norm[γ'[s]] - norm'[γ'[s]] γ᾽[s] γ''[x]/norm[γ᾽[s]]^2

You might note that γ[s] is not treated as a vector. More precisely, when the chain rule is applied to the derivative of norm[γ'[s]], γ'[s] is treated as a scalar.

N.B. There is a difference between Norm and the OP's norm. Norm assumes its arguments might be Complex, and uses Abs, which can complicate derivatives. For instance, with Norm in place of norm, normal[γ][1] // N has things like Derivative[1][Abs][0.540302] in the answer.

|improve this answer|||||

I'd define one function only, keep the built-in Norm and use pure functions:

derVec[\[Gamma]_] := 
Module[{local}, local = Derivative[1][\[Gamma]];
 Function[x, Evaluate[Simplify[local[x]/Norm[local[x]], Assumptions -> {x \[Element] Reals}

Example :

curve = Function[s, {Cos[s], Sin[s], s}];

{tangent, normal} = Rest@NestList[derVec, curve, 2]
|improve this answer|||||
  • $\begingroup$ This computes $\gamma''(s)/\lVert\gamma''(s)\rVert$, which is not the same as the normal. One can see this, for example, on the parabola $(s,s^2,0)$. $\endgroup$ – user484 Mar 3 '13 at 0:33
  • $\begingroup$ @RahulNarain True, should have thought about it, thanks. Will correct. $\endgroup$ – b.gates.you.know.what Mar 3 '13 at 0:41
  • $\begingroup$ @RahulNarain Done. $\endgroup$ – b.gates.you.know.what Mar 3 '13 at 10:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy