Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

I had an idea about how to answer this question and started putting some things together. I wanted to put something together that was fairly powerful and could handle simplification of linear expressions.

I should start by apologizing for the length of this example. It is quite long. But all my attempts at reducing it have either resulted in code that compiles, or results in code that has other issues which, when resolved seem to shed no light on what's happening here. I can say that the first portion of code I post below works reasonably well (it's not perfect, but it does get close to the job I want).

Regarding simplifying linear expressions, I got something that worked pretty well. Here's the code I put together to do that; I called the file containing this code *reduce_terms.tex*:

\ExplSyntaxOn
%-@-(1)------------------------------------------------------------------------------------
%% some SEVEN commands to help avoid errors in naming the "counters"
\cs_new:Npn \_stirling_var_get:n    #1   { \int_use:c  { l/stirling/var/ #1 /int } }
\cs_new:Npn \_stirling_var_create:n #1   { \int_new:c  { l/stirling/var/ #1 /int } }
\cs_new:Npn \_stirling_var_incr:nn  #1#2 { \int_add:cn { l/stirling/var/ #1 /int } {#2}}
\cs_new:Npn \_stirling_var_decr:nn  #1#2 { \int_sub:cn { l/stirling/var/ #1 /int } {#2}}
\cs_new:Npn \_stirling_var_unset:n  #1   
  { 
    \cs_undefine:c { l/stirling/var/ #1 /int } 
  }
\prg_new_conditional:Npnn \_stirling_var_if_exists:n #1 { T , F , TF }
  { 
    \int_if_exist:cTF { l/stirling/var/ #1 /int } 
      { \prg_return_true: } 
      { \prg_return_false: } 
  }
%%--------------------------------------------------------------------------------
\cs_new:Npn \_stirling_var_advance_counter_according_to_term_sign:nn #1#2
  {
    \bool_if:NTF \l_term_is_positive_bool
      { \_stirling_var_incr:nn { #1 }{ #2 } }
      { \_stirling_var_decr:nn { #1 }{ #2 } }
  }
%%--------------------------------------------------------------------------------
%% a crude attempt to determine whether a token is a digit
\prg_new_conditional:Npnn \is_digit:n #1 { p, T, F , TF }
  { \int_compare:nTF { `0 > `#1 }
                     { \prg_return_false: }
                     { \int_compare:nTF { `#1 > `9 }
                                        { \prg_return_false: }
                                        { \prg_return_true:  }
      }}

%-@-(2)------------------------------------------------------------------------------------
%%--------------------------------------------------------------------------------
\seq_new:N \l_stirling_ordered_list_of_all_variable_names_seq
\seq_new:N \l_stirling_list_of_plus_separated_tokens_seq
\seq_new:N \l_stirling_list_of_minus_separated_tokens_seq
%% boolean to track whether term was preceded by a "+" or "-"
\bool_new:N \l_term_is_positive_bool
%% token list to store the first term from a string of "-" separated items.
\tl_new:N  \l_stirling_lead_tl

%-@-(3)------------------------------------------------------------------------------------
\bool_new:N \l_stirling_have_not_hit_nondigit_token_yet_bool
\tl_new:N   \l_stirling_tmp_a
\tl_new:N   \l_stirling_tmp_b
\tl_new:N   \l_stirling_tmp_c

\cs_new:Npn \_stirling_var_read_coefficient:n #1 
  {
    \bool_set_true:N \l_stirling_have_not_hit_nondigit_token_yet_bool
    \tl_clear:N      \l_stirling_tmp_a
    \tl_clear:N      \l_stirling_tmp_b
    \tl_clear:N      \l_stirling_tmp_c
    \tl_set:Nf       \l_stirling_tmp_a { \tl_trim_spaces:n { #1 }}
    %% if expression starts with a negative sign, you don't want to mistake
    %% there will be an empty token passed.  You want to ignore this term 
    %% otherwise the constant term will be augmented.
    \tl_if_empty:NF \l_stirling_tmp_a
    {
      \tl_map_inline:Nn \l_stirling_tmp_a
        {
          %% collect all the digits to build the numerical coefficient.
          %% As soon as we hit something that is not a digit, stop and
          %% treat all remaining tokens as part of the variable name 
          %% regardless of whether later digits will occur.
          \bool_if:NTF \l_stirling_have_not_hit_nondigit_token_yet_bool
            {
              \is_digit:nTF { ##1 }
                {  \tl_put_right:Nn  \l_stirling_tmp_b { ##1 } }
                {  \bool_set_false:N \l_stirling_have_not_hit_nondigit_token_yet_bool
                   \tl_put_right:Nn  \l_stirling_tmp_c { ##1 } }
            } 
            {
              \tl_put_right:Nn \l_stirling_tmp_c { ##1 }
            } 
        }
      %% The contents of \l_stirling_tmp_b are to be used as a number.  If
      %% no digits were found, then this variable is empty.  If it is empty,
      %% then it should be set to the value of "1".
      \tl_if_empty:NT \l_stirling_tmp_b { \tl_set:Nn \l_stirling_tmp_b { 1 } }
      \_stirling_var_if_exists:nTF { \l_stirling_tmp_c } 
        { 
          \_stirling_var_advance_counter_according_to_term_sign:nn { \l_stirling_tmp_c } { \l_stirling_tmp_b}
        }
        {
          \_stirling_var_create:n { \l_stirling_tmp_c }
          \_stirling_var_advance_counter_according_to_term_sign:nn { \l_stirling_tmp_c } { \l_stirling_tmp_b}
          %% "x" may be stronger than you need.  But you do need to expand so that it's
          %% not the token \l_stirling_tmp_c sitting in the sequence.
          \seq_put_right:Nx \l_stirling_ordered_list_of_all_variable_names_seq { \l_stirling_tmp_c }
        }
    }
  }     

%-@-(4)------------------------------------------------------------------------------------
\cs_new:Npn \_parse_expression:n #1
  { %%\fbox{\ttfamily#1} 
    \__parse_addition:n {#1}
  }

\cs_new:Npn \__parse_addition:n #1
  {
    \seq_set_split:Nnn \l_stirling_list_of_plus_separated_tokens_seq { + } { #1 }
    \seq_map_function:NN \l_stirling_list_of_plus_separated_tokens_seq 
                         \__parse_subtraction:n
  }

\cs_new:Npn \__parse_subtraction:n #1
  {
      \seq_set_split:Nnn \l_stirling_list_of_minus_separated_tokens_seq { - } { #1 }
      \seq_pop_left:NN   \l_stirling_list_of_minus_separated_tokens_seq \l_stirling_lead_tl
      \bool_set_true:N   \l_term_is_positive_bool
      \_stirling_var_read_coefficient:n { \l_stirling_lead_tl }
      \bool_set_false:N  \l_term_is_positive_bool
      \seq_map_function:NN \l_stirling_list_of_minus_separated_tokens_seq
                           \_stirling_var_read_coefficient:n
  }

%-@-(5)------------------------------------------------------------------------------------
\cs_new:Npn \_reset_coefficient_counters:n #1
  {
    \seq_map_function:NN \l_stirling_ordered_list_of_all_variable_names_seq
                         \_stirling_var_unset:n 
  }

%-@-(6)------------------------------------------------------------------------------------
%%--------------------------------------------------------------------------------
\cs_new:Npn \_write_expression:n #1
  {
    \seq_map_function:NN \l_stirling_ordered_list_of_all_variable_names_seq
                         \_stirling_var_set_according_to_coefficient:n 
  }

\tl_new:N        \l_stirling_plus_sign_tl
\bool_new:N      \l_stirling_passed_first_term_bool
\bool_set_true:N \l_stirling_passed_first_term_bool

\cs_generate_variant:Nn \tl_put_right:Nn {Nf}
\cs_new:Npn \_stirling_var_set_according_to_coefficient:n #1
  {
    %% No "+" should precent the lead term
    \bool_if:NTF   \l_stirling_passed_first_term_bool
      { \tl_set:Nn \l_stirling_plus_sign_tl {   } }
      { \tl_set:Nn \l_stirling_plus_sign_tl { + } }
    \int_compare:nNnT { \_stirling_var_get:n { #1 } } > { 0 } { \l_stirling_plus_sign_tl }
    \int_case:nnn { \_stirling_var_get:n { #1 } }
      { {  0 } {     }
        {  1 } { \tl_if_empty:nTF {#1}{ 1 }{#1}  \bool_set_false:N \l_stirling_passed_first_term_bool}
        { -1 } { -#1                             \bool_set_false:N \l_stirling_passed_first_term_bool}
      } { \_stirling_var_get:n { #1 } #1         \bool_set_false:N \l_stirling_passed_first_term_bool}
    %% as soon as you've processed first coefficient, change
    %% the boolean that tracks this.  Otherwise, you may not
    %% have "+" signs where you believe they should be.  Also,
    %% without this boolean, you might have "+" where you'd prefer
    %% not to have them:  such as in front of the first term.
  }

\ExplSyntaxOff

After getting this much up and running, I thought it would be a simple task to create the partial derivatives. But I got stuck with the denominators. Here's the code that I wrote trying to get the denominator formatted correctly. I've embedded several of my approaches to get this right--some are clearly wrong--but nothing I do seems to work. I'm quite mystified.

The idea here is that I format each part of the denominator and then store the formatting in a sequence variable which will be called later on when I actually want to print out the derivative.

I've already tried posting a question early regarding this next portion of code. I was hoping I'd narrowed it down to something that would prove fruit. But alas, even if I managed to narrow it down correctly, I'm having difficulty getting the posted solutions to cooperate.

This following code I saved in a file I called *find_derivatives.tex*:

\ExplSyntaxOn
\seq_new:N  \l_stirling_derivative_denominator_raw_seq
\seq_new:N \l_stirling_denominator_pow_seq

\tl_new:N \l_stirling_tmp_d
\cs_generate_variant:Nn \tl_set:Nn {Nx}
\cs_generate_variant:Nn \seq_gput_right:Nn { Nf}
\cs_new:Npn \_parse_derivative:nn #1#2 
  {
    \_parse_expression:n {#2}
    %%---------------------------------------------------------------------------
    %% This next line gets it completely wrong                                   
    \seq_gput_right:Nn \l_stirling_denominator_pow_seq { d#1^{ \_write_expression:n {}  }}
    %%---------------------------------------------------------------------------
    %% Uncommenting this next set of lines will also result in an error.         
    %% This next approach gives me an "inaccessible" error.                      
    %%<2>%% \tl_set:Nx \l_stirling_tmp_d { \_write_expression:n {}}              
    %%<2>%% \seq_gput_right:Nx \l_stirling_denominator_pow_seq { d#1^{ \l_stirling_tmp_d  }}
    %%---------------------------------------------------------------------------
    %% Uncommenting this line, things are a bit better.  But this seems comletely
    %% contrary to what I want to do.                                            
    %%<3>%% \seq_gput_right:Nn \l_stirling_denominator_pow_seq { d#1^{ \_parse_expression:n {#2} \_write_expression:n {}  }}
    %%---------------------------------------------------------------------------
    %% This line shouldn't be here, but it shows the values that should          
    %% be appearing in the exponents, but which aren't.                          
    \fbox{\_write_expression:n{}}\,
    \_reset_coefficient_counters:n {}  
  }

\cs_new:Npn \parse_derivative:nn #1 #2 
  {
    \seq_set_split:Nnn \l_stirling_derivative_denominator_raw_seq { , } { #2 }
    \seq_map_inline:Nn \l_stirling_derivative_denominator_raw_seq
      {
        \group_begin:
        \tl_if_single:nTF { ##1 }
          { \_parse_derivative:nn { ##1 } { 1 } } 
          { \_parse_derivative:nn ##1 } 
        \group_end:
      } 

    \frac{#1}{ \seq_use:Nn \l_stirling_denominator_pow_seq {\,} }
  }    

\NewDocumentCommand{\derivative}{ mm }
    { \parse_derivative:nn {#1}{#2} }

\ExplSyntaxOff

Here's the master document:

\documentclass[12pt]{article}
\usepackage{amsmath,amssymb}
\usepackage{xparse}
\input{lib/reduce_terms}
\input{lib/find_derivatives}
\ExplSyntaxOn
%% Not quite overkill.
\cs_new:Npn \simplify_expression:n #1
  {
    \group_begin:
    \_parse_expression:n {#1}
    \_write_expression:n {}
    \_reset_coefficient_counters:n {}
    \group_end:
  }
\NewDocumentCommand{\simplifyExpression}{ m }
    { \simplify_expression:n {#1} }
\ExplSyntaxOff
\pagestyle{empty}
\begin{document}

\begin{align*}
   \simplifyExpression{12-x-3y-k-x-3-y-y-w+4w+10x-w2-7+5} \\
   \simplifyExpression{3a+x-2y-4x}                        \\
   \simplifyExpression{-3+7-x+2y}                         \\
   \simplifyExpression{-3}                                \\
   \simplifyExpression{1+x}                               \\
   \simplifyExpression{1}                                 \\
   \simplifyExpression{x}                                 \\
   \simplifyExpression{x+y-x}                             \\
\end{align*}

\[
  \derivative{x}{{v}{3},{w}{3k+2-k},y,{z}{m+k}}
\]

\end{document}

which results in

enter image description here

My question is about how to get the variables in the denominator to have the correct exponents. The expressions in the fboxs are what should be appearing as the exponents.

share|improve this question

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Browse other questions tagged or ask your own question.