1
$\begingroup$

There is a very convenient function Dt that allows to take a total differential of any expression, e.g.:

expr = (a + b c)/d;
Dt[expr]

(Dt[a] + c Dt[b] + b Dt[c])/d - ((a + b c) Dt[d])/d^2

Consider a case where expr is generic (it is not known a priori which and how many variables appear). I would like to have a function that extracts a list of all Dt[x_] from an expression. For the above example it should do:

extract[Dt[expr]]

{Dt[a],Dt[b],Dt[c],Dt[d]}

My own version of this function is:

extract[x_] := Select[Variables[x], (Head[#] === Dt) &]

but I feel like extracting all variables and then selecting a subset is too hacky. Is there a nice way to implement this?

EDIT

Bob Hanlon and Mr. Wizard suggested to use Cases and Union. Unfortunately, these functions appear to be slower than the above in some cases:

Select[Variables[ Dt[(a + b c)/d + (a + b c)/d q]], (Head[#] === Dt) &] // AbsoluteTiming

{0.0000811731, {Dt[a], Dt[b], Dt[c], Dt[d], Dt[q]}}

compared to

Cases[Dt[(a + b c)/d + (a + b c)/d q], _Dt, {-2}] // Union // AbsoluteTiming

{0.000136221, {Dt[a], Dt[b], Dt[c], Dt[d], Dt[q]}}

$\endgroup$
  • 1
    $\begingroup$ Cases[Dt[expr], _Dt, Infinity] // Union $\endgroup$ – Bob Hanlon May 22 '17 at 21:37
  • 2
    $\begingroup$ @BobHanlon Or slightly shorter: Cases[Dt[expr], _Dt, {-2}] // Union -- the single level search should be somewhat faster with large expressions as well. $\endgroup$ – Mr.Wizard May 22 '17 at 21:38
  • $\begingroup$ @Mr.Wizard BobHanlon Thank you for the suggestions! Unfortunately that does not yield a speed up. See the edit of my question. $\endgroup$ – Kagaratsch May 22 '17 at 21:45
  • 1
    $\begingroup$ Union@Cases[expr, HoldPattern[Dt[_]], Infinity] $\endgroup$ – Ali Hashmi May 22 '17 at 21:50
2
$\begingroup$

Of the various methods proposed in comments, plus a hybrid, I find my proposal fastest:

foo = Dt[(a + b c)/d + (a + b c)/d q + ((a + b c)/d + (a + b c)/d q)/z + 
    q r ((a + b c)/d + (a + b c)/d q + ((a + b c)/d + (a + b c)/d q)/z)];

Select[Variables @ foo, (Head[#] === Dt) &]      // RepeatedTiming

Cases[foo, _Dt, Infinity] // Union               // RepeatedTiming

foo // Variables // Cases[_Dt]                   // RepeatedTiming

Union@Cases[foo, HoldPattern[Dt[_]], Infinity]   // RepeatedTiming

Cases[foo, _Dt, {-2}] // Union                   // RepeatedTiming
{0.0000269, {Dt[a], Dt[b], Dt[c], Dt[d], Dt[q], Dt[r], Dt[z]}}

{0.00002049, {Dt[a], Dt[b], Dt[c], Dt[d], Dt[q], Dt[r], Dt[z]}}

{0.000019798, {Dt[a], Dt[b], Dt[c], Dt[d], Dt[q], Dt[r], Dt[z]}}

{0.00001882, {Dt[a], Dt[b], Dt[c], Dt[d], Dt[q], Dt[r], Dt[z]}}

{0.0000145, {Dt[a], Dt[b], Dt[c], Dt[d], Dt[q], Dt[r], Dt[z]}}
$\endgroup$
  • $\begingroup$ Guess, I was not using the correct timing function. Sorry about that! $\endgroup$ – Kagaratsch May 22 '17 at 22:02
  • $\begingroup$ Wouldn't the results be more accurate if you precede each line with ClearSystemCache[];? $\endgroup$ – Bob Hanlon May 23 '17 at 0:08
  • $\begingroup$ @BobHanlon I don't see why that would be needed in this case, and using it doesn't appear to change the result. Were we using something like Simplify I think it would be another story. $\endgroup$ – Mr.Wizard May 23 '17 at 9:08
  • $\begingroup$ On my system (Mac) I got a slightly different ordering using it. $\endgroup$ – Bob Hanlon May 23 '17 at 13:00
1
$\begingroup$
foo = 
Dt[(a + b c)/d + (a + b c)/d q + ((a + b c)/d + (a + b c)/d q)/z + 
 q r ((a + b c)/d + (a + b c)/d q + ((a + b c)/d + (a + b c)/d q)/
     z)];

Union@Level[foo, {-2}] // Cases[#, _Dt] &
(* {Dt[a], Dt[b], Dt[c], Dt[d], Dt[q], Dt[r], Dt[z]} *)

or

Select[Union@Level[expr, {-2}], (Head@# === Dt)&]
$\endgroup$

Your Answer

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

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