Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Equation numbering with sections #2427

Closed
bdtc opened this issue May 5, 2020 · 10 comments
Closed

Equation numbering with sections #2427

bdtc opened this issue May 5, 2020 · 10 comments
Labels
Accepted Issue has been reproduced by MathJax team Code Example Contains an illustrative code example, solution, or work-around v3
Milestone

Comments

@bdtc
Copy link

bdtc commented May 5, 2020

The current documentation for tagFormat includes an example of equation numbering with sections, i.e. Eq (5.3)

MathJax = {
  section: 1,
  tex: {
    tagFormat: {
      number: (n) => MathJax.section + '.' + n,
      id: (n) => 'eqn-id-' + n
    }
  }
};

The example fails when using an equation with a \ref, probably due to delayed rendering to create the reference. The final result for the equation displays the section number MathJax.section as it existed at the end of the document, instead of the section number when the equation occurred, which is a problem if the section number changes mid-document.

The equation number, 'n', does not exhibit this problem. It seems to be stored at the first instance for future use at the time of rendering.

Request: A section number which could be set and later adjusted mid-document, and stored in the same way as the equation number such that it would be correct if a reference is used, along with a user-level interface to perform the adjustment, in the form of a TeX macro.

Also: A user-level interface to adjust the equation number mid-document. (I see that it is this.counter, but what must I use for <this> if I wish to set it?)

(Use case: The LaTeX Lwarp package has support for MathJax for 50+ LaTeX packages, as well as many other packages which cannot be processed by MathJax. Some of these other packages use equation numbers, and the results are converted to SVG images, but the MathJax equation number must be adjusted to resynchronize MathJax after each non-MathJax equation. The section number must also be set sometimes as well. I currently use a solution for setting equation numbers for MathJax v2, but it also fails for section numbers if \ref is used. Finally, when Lwarp can use v3, I can add support for starred macros for packages such as physics and mathtools.)

@dpvc
Copy link
Member

dpvc commented May 5, 2020

I am not able to reproduce your issue. Here is an example file that contains two sections and references to both at the end.

<!DOCTYPE html>
<html>
<head>
<meta content="width=device-width, initial-scale=1" name="viewport"/>
<meta charset="utf-8"/>
<title>Section numbers</title>
<script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>
<script>
MathJax = {
  loader: {load: ['[tex]/tagformat']},
  section: 1,
  tex: {
    tags: 'ams',
    packages: {'[+]': ['tagformat']},
    tagformat: {
      number: (n) => MathJax.config.section + '.' + n
    }
  },
  startup: {
    typeset: false,
    pageReady() {
      MathJax.typeset(['#section1']);
      MathJax.config.section = 2
      MathJax.startup.input[0].parseOptions.tags.counter = 0;
      MathJax.startup.input[0].parseOptions.tags.allCounter = 0;
      MathJax.typeset(['#section2']);
      MathJax.typeset(['#refs']);
    }
  }
};</script>
<script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"></script>
</head>
<body>

<div id="section1">
\begin{equation}x+1\label{e1}\end{equation}
</div>

<div id="section2">
\begin{equation}x+1\label{e2}\end{equation}
</div>

<div id="refs">
References to \eqref{e1} and \eqref{e2}
</div>

</body>
</html>

and this is the output:

sections

There is an error in the documentation (you must use Mathjax.config.section not just MathJax.section), which I will fix. You may already have discovered that.

Here, we prevent the initial typesetting by setting MathJax.startup.typeset to false, and then do our own typesetting section by section in the MathJax.startup.pageReady() function. We typeset section 1, then set the section to section 2, reset the tag counters, and typeset section 2. Finally, we typeset the reference section to show that the labels are correct and refer to the correct equations.

So all this seems to work as expected (modulo the error, and knowing how to reset the equation counters).

If you want a TeX macro to advance the section number (so you don't have to do all the separate typesetting commands), you could do the following:

<!DOCTYPE html>
<html>
<head>
<meta content="width=device-width, initial-scale=1" name="viewport"/>
<meta charset="utf-8"/>
<title>Section numbers</title>
<script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>
<script>
MathJax = {
  loader: {load: ['[tex]/tagformat']},
  section: 1,
  tex: {
    tags: 'ams',
    packages: {'[+]': ['tagformat', 'sections']},
    tagformat: {
      number: (n) => MathJax.config.section + '.' + n
    }
  },
  startup: {
    ready() {
      const Configuration = MathJax._.input.tex.Configuration.Configuration;
      const CommandMap = MathJax._.input.tex.SymbolMap.CommandMap;
      new CommandMap('sections', {
        nextSection: 'NextSection'
      }, {
        NextSection(parser, name) {
          MathJax.config.section++;
          parser.tags.counter = parser.tags.allCounter = 0;
        }
      });
      Configuration.create(
        'sections', {handler: {macro: ['sections']}}
      );
      MathJax.startup.defaultReady();
    }
  }
};</script>
<script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"></script>
</head>
<body>

\begin{equation}x+1\label{e1}\end{equation}

<div style="display:none">\(\nextSection\)</div>

\begin{equation}x+1\label{e2}\end{equation}

References to \eqref{e1} and \eqref{e2}

</body>
</html>

Here, we create a new package for the section macro (you could add other ones here to set the section to a specific value, etc.). The CommandMap sets up the \nextSection macro to advance the section number and clear the counters, and the Configuration section creates the sections package, which we include the tex.packages option.

The line

<div style="display:none">\(\nextSection\)</div>

advances the section number while not showing anything in the HTML file. It's a bit awkward, but works. Alternatively, one could include \nextSection in the first \begin{equation} in the section. This could, of course, be made into an actual extension file (see the custom tex extension example in the customization documentation) rather than a configuration.

If these don't resolve your problem, then you will have to provide more explicit code to illustrate what you are doing.

@bdtc
Copy link
Author

bdtc commented May 5, 2020

Expanding on your second example. The section number of the second equation is typeset to the section number of the last equation in the document.

<!DOCTYPE html>
<html>
<head>
<meta content="width=device-width, initial-scale=1" name="viewport"/>
<meta charset="utf-8"/>
<title>Section numbers</title>
<script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>
<script>
MathJax = {
  loader: {load: ['[tex]/tagFormat']},
  section: 1,
  tex: {
    tags: 'ams',
    packages: {'[+]': ['tagFormat', 'sections']},
    tagFormat: {
      number: (n) => MathJax.config.section + '.' + n,
      id: (n) => 'eqn-id-' + n
    }
  },
  startup: {
    ready() {
      const Configuration = MathJax._.input.tex.Configuration.Configuration;
      const CommandMap = MathJax._.input.tex.SymbolMap.CommandMap;
      new CommandMap('sections', {
        nextSection: 'NextSection'
      }, {
        NextSection(parser, name) {
          MathJax.config.section++;
          parser.tags.counter = parser.tags.allCounter = 0;
        }
      });
      Configuration.create(
        'sections', {handler: {macro: ['sections']}}
      );
      MathJax.startup.defaultReady();
    }
  }
};</script>
<script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"></script>
</head>
<body>

\begin{equation}x+1\label{e1}\end{equation}

\begin{equation}x+2\qquad\eqref{e3}\label{e2}\end{equation}

<div style="display:none">\(\nextSection\)</div>

\begin{equation}x+3\label{e3}\end{equation}

\begin{equation}x+4\label{e4}\end{equation}

<div style="display:none">\(\nextSection\)</div>

\begin{equation}x+5\label{e5}\end{equation}

References to \eqref{e1} and \eqref{e2}

</body>
</html>

Also, perhaps the equation id should include the section number.

@dpvc
Copy link
Member

dpvc commented May 6, 2020

OK, thanks. It is the forward reference that is causing the issue to occur. When a forward reference is included in an expression, MathJax can't process the expression until after the later expression is processed since it doesn't know the value of the tag for the referenced expression. So MathJax holds those for later and process them again at the end. That is why the section number is the final section number.

I will need to look at that process more closely to see how to handle it better.

@dpvc dpvc added Accepted Issue has been reproduced by MathJax team Investigate and removed Cannot Reproduce labels May 6, 2020
@dpvc
Copy link
Member

dpvc commented May 7, 2020

It turns out that MathJax was already caching the equation number counter, and we can piggyback on that to cache the section number as well. To do that you need to add a TeX post-filter that records the section number, and a pre-filter that sets the section number when the equation is being re-typeset. To do that, add the lines

      MathJax.startup.input[0].preFilters.add(({math}) => {
        if (math.inputData.recompile) MathJax.config.section = math.inputData.recompile.section;
      });
      MathJax.startup.input[0].postFilters.add(({math}) => {
        if (math.inputData.recompile) math.inputData.recompile.section = MathJax.config.section;
      });

after the MathJax.startup.defaultReady() command in the ready() function. Here is the complete file:

<!DOCTYPE html>
<html>
<head>
<meta content="width=device-width, initial-scale=1" name="viewport"/>
<meta charset="utf-8"/>
<title>Section numbers</title>
<script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>
<script>
MathJax = {
  loader: {load: ['[tex]/tagformat']},
  section: 1,
  tex: {
    tags: 'ams',
    packages: {'[+]': ['tagformat', 'sections']},
    tagformat: {
      number: (n) => MathJax.config.section + '.' + n
    }
  },
  startup: {
    ready() {
      const Configuration = MathJax._.input.tex.Configuration.Configuration;
      const CommandMap = MathJax._.input.tex.SymbolMap.CommandMap;
      new CommandMap('sections', {
        nextSection: 'NextSection'
      }, {
        NextSection(parser, name) {
          MathJax.config.section++;
          parser.tags.counter = parser.tags.allCounter = 0;
        }
      });
      Configuration.create(
        'sections', {handler: {macro: ['sections']}}
      );
      MathJax.startup.defaultReady();
      MathJax.startup.input[0].preFilters.add(({math}) => {
        if (math.inputData.recompile) MathJax.config.section = math.inputData.recompile.section;
      });
      MathJax.startup.input[0].postFilters.add(({math}) => {
        if (math.inputData.recompile) math.inputData.recompile.section = MathJax.config.section;
      });
   }
  }
};</script>
<script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"></script>
</head>
<body>

\begin{equation}x+1\label{e1}\end{equation}

\begin{equation}x+2\qquad\eqref{e3}\label{e2}\end{equation}

<div style="display:none">\(\nextSection\)</div>

\begin{equation}x+3\label{e3}\end{equation}

\begin{equation}x+4\label{e4}\end{equation}

<div style="display:none">\(\nextSection\)</div>

\begin{equation}x+5\label{e5}\end{equation}

References to \eqref{e1} and \eqref{e2}

</body>
</html>

I will update the documentation to include this.

@dpvc
Copy link
Member

dpvc commented May 7, 2020

Also, perhaps the equation id should include the section number.

The id is actually constructed from the label, if there is one, and from the actual tag (which includes the section number) if not. So if e4 were unlabeled, for instance, the id would be eqn-id-2.2. The n in the id() function is not really a number, but is the label or tag value.

@bdtc
Copy link
Author

bdtc commented May 9, 2020

This is working well now, thanks. I also added support for amsmath subequations and \numberwithin with chapters, sections, and subsections.

@dpvc dpvc added this to the 3.1.3 milestone Jan 31, 2021
@dpvc dpvc self-assigned this Jan 31, 2021
@dpvc dpvc added the Code Example Contains an illustrative code example, solution, or work-around label Apr 1, 2021
@dpvc dpvc removed their assignment Apr 2, 2021
@dpvc dpvc closed this as completed Apr 2, 2021
@parhizkari
Copy link

Hi Davide
your example code above works in Mathjax 3.1.4 but not as expeted in v. 3.2.0.
When I use <script src="https://cdn.jsdelivr.net/npm/mathjax@3.1.4/es5/tex-chtml.js"></script> it is fine, but using <script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"></script> it writes in the console that "MathJax: Invalid option "tagFormat" (no default value)." So it resets the numbering at each section, but the section number disappear from equation number.

@dpvc
Copy link
Member

dpvc commented May 9, 2022

The example page works for me in 3.2. Did you modify the page, and in particular, the configuration? The message you are receiving suggests that you haven't told the TeX input jax to use the tagformat extension, so make sure that

  loader: {load: ['[tex]/tagFormat']},

and

  tex: {
    packages: {'[+]': ['tagFormat', 'sections']},
  },

are both in your configuration. If you haven't set the packages properly, that would produce the message you are indicating.

@parhizkari
Copy link

parhizkari commented May 15, 2022

Hi and sorry for being late on this,
When I copy your very example above with no alteration:

<!DOCTYPE html>
<html>
<head>
<meta content="width=device-width, initial-scale=1" name="viewport"/>
<meta charset="utf-8"/>
<title>Section numbers</title>
<script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>
<script>
MathJax = {
  loader: {load: ['[tex]/tagFormat']},
  section: 1,
  tex: {
    tags: 'ams',
    packages: {'[+]': ['tagFormat', 'sections']},
    tagFormat: {
      number: (n) => MathJax.config.section + '.' + n,
      id: (n) => 'eqn-id-' + n
    }
  },
  startup: {
    ready() {
      const Configuration = MathJax._.input.tex.Configuration.Configuration;
      const CommandMap = MathJax._.input.tex.SymbolMap.CommandMap;
      new CommandMap('sections', {
        nextSection: 'NextSection'
      }, {
        NextSection(parser, name) {
          MathJax.config.section++;
          parser.tags.counter = parser.tags.allCounter = 0;
        }
      });
      Configuration.create(
        'sections', {handler: {macro: ['sections']}}
      );
      MathJax.startup.defaultReady();
      MathJax.startup.input[0].preFilters.add(({math}) => {
        if (math.inputData.recompile) MathJax.config.section = math.inputData.recompile.section;
      });
      MathJax.startup.input[0].postFilters.add(({math}) => {
        if (math.inputData.recompile) math.inputData.recompile.section = MathJax.config.section;
      });
   }
  }
};</script>
<script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"></script>
</head>
<body>

\begin{equation}x+1\label{e1}\end{equation}

\begin{equation}x+2\qquad\eqref{e3}\label{e2}\end{equation}

<div style="display:none">\(\nextSection\)</div>

\begin{equation}x+3\label{e3}\end{equation}

\begin{equation}x+4\label{e4}\end{equation}

<div style="display:none">\(\nextSection\)</div>

\begin{equation}x+5\label{e5}\end{equation}

References to \eqref{e1} and \eqref{e2}

</body>
</html>

I receive
image
and when I alter it to use the older version of Mathjax like

<!DOCTYPE html>
<html>
<head>
<meta content="width=device-width, initial-scale=1" name="viewport"/>
<meta charset="utf-8"/>
<title>Section numbers</title>
<script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>
<script>
MathJax = {
  loader: {load: ['[tex]/tagFormat']},
  section: 1,
  tex: {
    tags: 'ams',
    packages: {'[+]': ['tagFormat', 'sections']},
    tagFormat: {
      number: (n) => MathJax.config.section + '.' + n,
      id: (n) => 'eqn-id-' + n
    }
  },
  startup: {
    ready() {
      const Configuration = MathJax._.input.tex.Configuration.Configuration;
      const CommandMap = MathJax._.input.tex.SymbolMap.CommandMap;
      new CommandMap('sections', {
        nextSection: 'NextSection'
      }, {
        NextSection(parser, name) {
          MathJax.config.section++;
          parser.tags.counter = parser.tags.allCounter = 0;
        }
      });
      Configuration.create(
        'sections', {handler: {macro: ['sections']}}
      );
      MathJax.startup.defaultReady();
      MathJax.startup.input[0].preFilters.add(({math}) => {
        if (math.inputData.recompile) MathJax.config.section = math.inputData.recompile.section;
      });
      MathJax.startup.input[0].postFilters.add(({math}) => {
        if (math.inputData.recompile) math.inputData.recompile.section = MathJax.config.section;
      });
   }
  }
};</script>
<script src="https://cdn.jsdelivr.net/npm/mathjax@3.1.4/es5/tex-chtml.js"></script>
</head>
<body>

\begin{equation}x+1\label{e1}\end{equation}

\begin{equation}x+2\qquad\eqref{e3}\label{e2}\end{equation}

<div style="display:none">\(\nextSection\)</div>

\begin{equation}x+3\label{e3}\end{equation}

\begin{equation}x+4\label{e4}\end{equation}

<div style="display:none">\(\nextSection\)</div>

\begin{equation}x+5\label{e5}\end{equation}

References to \eqref{e1} and \eqref{e2}

</body>
</html>

I receive
image

Am I missing a point?

PS. Beside that, if one uses the svg-full version, the loader needs not be there as tagFormat will already be loaded, but yet packages: {'[+]': ['sections']} is required as otherwise \nextSection command will not be recognized. Any chance sections will be added automatically in the svg-full version?

@dpvc
Copy link
Member

dpvc commented May 16, 2022

Ah, sorry, I wasn't looking carefully. You are right the code above does need to be changed. The reason is that in version 3.1.0, the names of extensions were normalized so that the file names and extensions names were the same, and all lower case (see mathjax/MathJax-src#485). We included backward-compatibility code in v3.1 so that the older names would still work (see What's new in v3.1, in order to allow people time to update their configurations, but this was removed in v3.2 (see the release notes for details).

So the fix here is to change tagFormat to tagformat throughout. I will update the example above so it is correct for future readers.

if one uses the svg-full version, the loader needs not be there as tagFormat will already be loaded, but yet packages: {'[+]': ['sections']} is required as otherwise \nextSection command will not be recognized. Any chance sections will be added automatically in the svg-full version?

The sections package is not a standard package in MathJax, and it is being defined in the ready() function by the command

      Configuration.create(
        'sections', {handler: {macro: ['sections']}}
      );

in order to define the \nextSection macro that is created in

      new CommandMap('sections', {
        nextSection: 'NextSection'
      }, {
        NextSection(parser, name) {
          MathJax.config.section++;
          parser.tags.counter = parser.tags.allCounter = 0;
        }
      });

so there is only a sections package because of these commands in the ready() function. That's why it is not included in the svg-full component.

It would be possible, of course, to create an extension that adds this macro, but that is not currently planned. If you, or someone reading this, wants to put one together and contribute it, we would be happy to consider it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Accepted Issue has been reproduced by MathJax team Code Example Contains an illustrative code example, solution, or work-around v3
Projects
None yet
Development

No branches or pull requests

3 participants