19

please look at the following MWE:

\documentclass{article}

\usepackage{xparse}

\NewDocumentCommand\pdiff{s o m O{1} g O{1} g O{1}}% Star, function, variable 1, how many differentiations, ...
 {\newcount\pdiffn% Count the number of differentiations
  \advance\pdiffn #4\relax%
  \IfValueT{#5}{\advance\pdiffn #6\relax}%
  \IfValueT{#7}{\advance\pdiffn #8\relax}%
  \frac%
   {\partial \ifnum\pdiffn >1\relax ^{\the\pdiffn} \fi%
    \IfValueT{#2}{#2}}%
   {\IfValueT{#7}{\mathinner{\partial #7 \ifnum#8 >1\relax ^{#8} \fi}}%
    \IfValueT{#5}{\mathinner{\partial #5 \ifnum#6 >1\relax ^{#6} \fi}}%
    \mathinner{\partial #3 \ifnum#4 >1\relax ^{#4} \fi}}}

\begin{document}

\Huge

\[ \pdiff[f(x,y,z)]{y} \]

\[ \pdiff[f(x,y,z)]{y}[3]{x}{z} \]

\[ \pdiff[f(x,y,z)]{y}[9999]{x}[3333]{z}[8888] \]

\end{document}

(Ignore the star type argument, I used it for crating a flat frac (not necessary now).)

This works well if the differentiation is restricted to 3 variables. Is there a possibility to extend this macro to more variables? This way it will not work, because there are already 8 arguments.

I know "SplitList" and "ProcessList" but here it is more complicated because I have to add the number of differentiations for each variable.

Any ideas?

enter image description here

13

A listofitems approach that uses key values. The syntax is, for example

\pdiff[y=3,x,z,p=2,d=3,q,r=2,s]{f(x,y,z,p,d,q,r,s)}

Here is the MWE

\documentclass{article}
\usepackage{listofitems,tikz}
\newcounter{totalindex}
\newcommand\pdiff[2][1]{%
  \setsepchar{,/=}%
  \readlist\Partialvars{#1}%
  \setcounter{totalindex}{0}%
  \foreach \Varindex in {1,...,\listlen\Partialvars[]}%
    {%
      \ifnum\listlen\Partialvars[\Varindex]>1\relax%
        \addtocounter{totalindex}{\Partialvars[\Varindex,2]}%
      \else%
        \stepcounter{totalindex}%
      \fi%
    }%
    \frac{\partial\ifnum\thetotalindex>1\relax^{\thetotalindex}\fi#2}%
         {%
          \foreach\Varindex in {1,...,\listlen\Partialvars[]}%
           {%
             \partial\Partialvars[\Varindex,1]%
             \ifnum\listlen\Partialvars[\Varindex]>1\relax^{\Partialvars[\Varindex,2]}\fi%
           }%
         }%
}
\begin{document}
\[ \pdiff[y]{f(x,y,z)} \]
\[ \pdiff[y=3,x,z]{f(x,y,z)} \]
\[ \pdiff[y=9999, x=3333, z=8888]{f(x,y,z)} \]
\[ \pdiff[y=3,x,z,p=2,d=3,q,r=2,s]{f(x,y,z,p,d,q,r,s)} \]
\end{document}

enter image description here

While the tikz \foreach loop, used above, is a readily understandable construct for looping through an index, one can eliminate the use of the tikz package altogether, using the looping construct built in to the listofitems package. In this case though, the loop is through the items of the list, and one can indirectly access the index number by way of \<loop-variable>cnt.

\documentclass{article}
\usepackage{listofitems}
\newcounter{totalindex}
\newcommand\pdiff[2][1]{%
  \setsepchar{,/=}%
  \readlist\Partialvars{#1}%
  \setcounter{totalindex}{0}%
  \foreachitem \Var \in \Partialvars[]%
    {%
      \ifnum\listlen\Partialvars[\Varcnt]>1\relax%
        \addtocounter{totalindex}{\Partialvars[\Varcnt,2]}%
      \else%
        \stepcounter{totalindex}%
      \fi%
    }%
    \frac{\partial\ifnum\thetotalindex>1\relax^{\thetotalindex}\fi#2}%
         {%
          \foreachitem \Var \in \Partialvars%
           {%
             \partial\Partialvars[\Varcnt,1]%
             \ifnum\listlen\Partialvars[\Varcnt]>1\relax^{\Partialvars[\Varcnt,2]}\fi%
           }%
         }%
}
\begin{document}
\[ \pdiff[y]{f(x,y,z)} \]
\[ \pdiff[y=3,x,z]{f(x,y,z)} \]
\[ \pdiff[y=9999, x=3333, z=8888]{f(x,y,z)} \]
\[ \pdiff[y=3,x,z,p=2,d=3,q,r=2,s]{f(x,y,z,p,d,q,r,s)} \]
\end{document}
  • 1
    I thank the OP for the acceptance. I would note that the optional argument really isn't "optional", as one wishes to always differentiate with respect to something. I should probably have made the default optional argument ? rather than a nonsensical 1, to remind the user to add the differentiated variable. – Steven B. Segletes Apr 14 '17 at 17:55
  • OK, as I have not found yet a document that explains the Latex 3 code your answer the onliest I can understand now. I took your idea and modified it a Little bit. :) – user125730 Apr 14 '17 at 19:08
  • @user125730 I don't understand LaTeX3 either...:^) – Steven B. Segletes Apr 14 '17 at 19:19
  • 1
    I think you can use \foreach by loading \usepackage{pgffor}, without pulling in the whole tikz library. Not sure if that saves you any time though! – David R Apr 25 '17 at 21:42
22

Needless to reinvent the wheel: there already exists a dedicated package: esdiff, which also manages evaluation points as indices:

\documentclass{article}

\usepackage{esdiff}

\begin{document}

    \[ \diffp{f(x, y, z)}{{x^{3333}}{y^{9999}}{z^{8888}}{t^{55555}}} \]%

    \[ \diffp*{f(x, y, z)}{{x^{3333}}{y^{9999}}{z^{8888}}{t^{55555}}}{(x_0,y_0,z_0,t_0)} \]%

\end{document} 

enter image description here

  • You need a blank line to get your \documentclass{article} into your code rather than the preceding text. – LSpice Apr 14 '17 at 18:34
13

You can use a comma separated list for the list of variables. I use the convention that

x/2,y/3,z

stands for “twice x, three times y and once z”. You can also use x/2,y/3,z/1, if you prefer.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\pdiff}{smm}
 {
  \IfBooleanTF{#1}
   {% with \pdiff* call the inner function for inline
    \egreg_pdiff_inline:nn { #2 } { #3 } % TO DO
   }
   {% with \pdiff call the inner function for display
    \egreg_pdiff_frac:nn { #2 } { #3 }
   }
 }

% allocate some variables
\int_new:N \l__egreg_pdiff_total_int
\seq_new:N \l__egreg_pdiff_vars_seq
\seq_new:N \l__egreg_pdiff_var_seq
\tl_new:N \l__egreg_pdiff_denom_tl

\cs_new_protected:Nn \egreg_pdiff_frac:nn
 {
  % clear the variables to be given values later
  \int_zero:N \l__egreg_pdiff_total_int
  \tl_clear:N \l__egreg_pdiff_denom_tl
  % split the second argumen at commas
  \seq_set_split:Nnn \l__egreg_pdiff_vars_seq { , } { #2 }
  % map this sequence for adding to the denominator and
  % computing the number of derivatives
  \seq_map_function:NN \l__egreg_pdiff_vars_seq \__egreg_pdiff_var:n

  % now print:
  % \l__egreg_pdiff_total_int is set to the number of derivatives
  % \l__egreg_pdiff_denom_tl contains the denominator
  \frac
   {
    \partial
    \int_compare:nT { \l__egreg_pdiff_total_int > 1 }
     { \sp { \int_to_arabic:n { \l__egreg_pdiff_total_int } } }
    #1
   }
   {
    \tl_use:N \l__egreg_pdiff_denom_tl
   }
 }

\cs_new_protected:Nn \__egreg_pdiff_var:n
 {
  % split the argument at / (it should be in the form 'x' or 'x/2')
  \seq_set_split:Nnn \l__egreg_pdiff_var_seq { / } { #1 }
  % if the sequence has one term, no exponent is present
  \int_compare:nTF { \seq_count:N \l__egreg_pdiff_var_seq < 2 }
   {% simple variable: add to the denominator and increment the number
    \tl_put_right:Nn \l__egreg_pdiff_denom_tl { \partial #1 }
    \int_incr:N \l__egreg_pdiff_total_int
   }
   {% multiple: add to the denominator and increment the number
    % item 1 contains the variable, item 2 the exponent
    \tl_put_right:Nx \l__egreg_pdiff_denom_tl
     {
      \partial
      \seq_item:Nn \l__egreg_pdiff_var_seq { 1 }
      \sp { \seq_item:Nn \l__egreg_pdiff_var_seq { 2 } }
     }
    \int_add:Nn \l__egreg_pdiff_total_int { \seq_item:Nn \l__egreg_pdiff_var_seq { 2 } }
   }
 }
\ExplSyntaxOff

\begin{document}

\[
\pdiff{f(x,y)}{x}
\quad
\pdiff{f(x,y,z)}{x/20,y/32,z}
\]

\end{document}

enter image description here

  • Really nice, but I wished, there was a tutorial for the Latex 3 code. :) There ist not any part I am able to understand. – user125730 Apr 14 '17 at 19:11
  • @user125730 I've added some comments – egreg Apr 14 '17 at 20:11
11

Update See a better version at the end.

Although there are packages to achieve this, I present another way, since xparse is already in charge here ;-)

A way with expl3 splitting a list of derivative specifiers and building a token list:

Syntax: \pdiff{x/3,y/4,z/17}{f(x,y,z)} would specify third deriv. with respect to x, the fourth with respect to y and the 17th regarding z.

Omitting the power or the / will be interpreted as derivative order 1.

(A check for accidental negative numbers should be added)

The function argument is not evaluated or checked.

The number of derivatives is calculated internally.

\documentclass{article}

\usepackage{xparse}

\ExplSyntaxOn

\cs_generate_variant:Nn \int_add:Nn {Nx}
\seq_new:N \l_pdiff_deriv_seq

\NewDocumentCommand{\pdiffs}{mm}{%
  \int_zero:N \l_tmpa_int
  \seq_set_from_clist:Nn \l_tmpa_seq {#1} % Split the comma list
  \seq_map_inline:Nn \l_tmpa_seq {% Traverse the local entries
    \seq_set_split:Nnn \l_tmpb_seq {/} {##1}% Split into variable and order
    \tl_put_right:Nn \l_tmpa_tl {\partial}% Built the nominator list
    \int_compare:nNnTF {\seq_count:N \l_tmpb_seq } = {\c_one} {% Is there only one split element?
      \seq_put_right:Nn \l_tmpb_seq {\c_one}% Yes
      \tl_put_right:Nx \l_tmpa_tl {\seq_item:Nn \l_tmpb_seq {1}} % Add now 'power'
    }{%
      \int_compare:nNnTF {\seq_item:Nn \l_tmpb_seq {2}} = {\c_one } {
        % Is the derivative order one --> no power? 
        \tl_put_right:Nx \l_tmpa_tl {\seq_item:Nn \l_tmpb_seq {1}}
      }{
        \tl_put_right:Nx \l_tmpa_tl {\seq_item:Nn \l_tmpb_seq {1}^{\seq_item:Nn \l_tmpb_seq  {2}}}
      }
    }
    \int_add:Nx \l_tmpa_int {\seq_item:Nn \l_tmpb_seq {2}}
  }
  % Display the list
  \mathinner{\frac{\partial^{\int_use:N\l_tmpa_int}#2}{\tl_use:N \l_tmpa_tl}}
}
\ExplSyntaxOff


\begin{document}

\Huge

\[\pdiffs{x/5,y/3}{f(x,y,z)}\]


\[\pdiffs{x/4,y/8,z/17}{f(x,y,z)}\]


\end{document}

enter image description here

Improved version with checks and autoexpansion of the variables:

\documentclass{article}

\usepackage{xparse}

\ExplSyntaxOn

\cs_generate_variant:Nn \int_add:Nn {Nx}
\cs_generate_variant:Nn \int_set:Nn {Nx}

\seq_new:N \l_pdiff_var_seq



\msg_new:nnn {pdiff}{negativedifforder}{Error:~The~order~of~derivative~is~negative~for~variable~#1!} 


\NewDocumentCommand{\pdiffs}{mm}{%
  \int_zero:N \l_tmpa_int
  \tl_clear:N \l_tmpa_tl
  \seq_clear:N \l_pdiff_var_seq
  \seq_set_from_clist:Nn \l_tmpa_seq {#1}
  \seq_map_inline:Nn \l_tmpa_seq {%
    \seq_set_split:Nnn \l_tmpb_seq {/} {##1}
    \seq_put_right:Nx \l_pdiff_var_seq {\seq_item:Nn \l_tmpb_seq {1}}
    \int_compare:nNnTF {\seq_count:N \l_tmpb_seq } = {\c_one} {
      \int_set:Nn \l_tmpb_int {\c_one}
      \seq_put_right:Nn \l_tmpb_seq {\c_one}
      \tl_put_right:Nn \l_tmpa_tl {\partial}
      \tl_put_right:Nx \l_tmpa_tl {\seq_item:Nn \l_tmpb_seq {1}}
    }{%
      \int_set:Nx \l_tmpb_int {\seq_item:Nn \l_tmpb_seq {2}}
      \int_compare:nNnTF { \l_tmpb_int } < {\c_zero} {
        \msg_fatal:nnx {pdiff}{negativedifforder}{\seq_item:Nn \l_tmpb_seq {1}}
      }{%
        \int_compare:nNnF {\l_tmpb_int } = {\c_zero} {%
          \tl_put_right:Nn \l_tmpa_tl {\partial}
          \int_compare:nNnTF{ \l_tmpb_int } = {\c_one } {
            \tl_put_right:Nx \l_tmpa_tl {\seq_item:Nn \l_tmpb_seq {1}}
          }{%
            \tl_put_right:Nx \l_tmpa_tl {\seq_item:Nn \l_tmpb_seq {1}^{\seq_item:Nn \l_tmpb_seq  {2}}}
          }
        }
      }
    }
    \int_add:Nx \l_tmpa_int {\l_tmpb_int}
  }
  \mathinner{\frac{\partial^{\int_use:N\l_tmpa_int}#2(\seq_use:Nn \l_pdiff_var_seq {,})}{\tl_use:N \l_tmpa_tl}}
}
\ExplSyntaxOff


\begin{document}

\Huge

\[\pdiffs{x/5,y/3,z/0}{f}\]


\[\pdiffs{x/4,y/8,z/17}{f}\]


\end{document}
  • 2
    I guess you want to present another way, not to prevent (competitors?) ;o) – Bernard Apr 14 '17 at 16:18
  • @Bernard: Thanks for the remark, I corrected the typo :D – user31729 Apr 14 '17 at 18:29
  • You're welcome. Maybe, it was a Freudian slip? ;o) – Bernard Apr 14 '17 at 18:32

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.