TransWikia.com

Cleveref sorting doesn't work correctly if theorem shares subsection counter

TeX - LaTeX Asked on June 20, 2021

In the MWE below, the theorems should be sorted as "1.2 and 2.1" rather than "2.1 and 1.2". The correct sorting behavior is obtained by replacing sibling=subsection with parent=section but of course then the theorem counter is not shared with subsection.

documentclass{article}

usepackage{thmtools}
usepackage{cleveref}

declaretheorem[sibling=subsection]{theorem}
%declaretheorem[parent=section]{theorem}

begin{document}

section{}
begin{theorem}
Blah.
end{theorem}

begin{theorem}label{theorem}
Blah.
end{theorem}

section{}
begin{theorem}label{othertheorem}
Blah.
end{theorem}

Cref{theorem,othertheorem}.

end{document}

I suppose the issue arises from a difference in how a subsection label is written to the aux file vs a new theorem counter, but how do I make this work correctly?

3 Answers

Note. The bug below will likely be fixed in the next release. https://github.com/muzimuzhi/thmtools/commit/0f9a1cf0ecdf26b4d1a5c6fd8fe27c912e919d5a

Actually, digging through the cleveref code a bit more, I am now convinced that this is a bug in thmtools.

Consider the following code:

documentclass{article}
usepackage{amsthm}
%usepackage{thmtools}
usepackage{cleveref}

newtheorem{theorem}[subsection]{Theorem}


begin{document}

section{}
begin{theorem}
Blah.
end{theorem}

begin{theorem}label{theorem}
Blah.
end{theorem}

section{}
begin{theorem}label{othertheorem}
Blah.
end{theorem}

Cref{theorem,othertheorem}.

end{document}

With thmtools not loaded, the aux file shows the correct output.

newlabel{theorem}{{1.2}{1}}
newlabel{theorem@cref}{{[theorem][2][1]1.2}{[1][1][]1}}
...
newlabel{othertheorem}{{2.1}{1}}
newlabel{othertheorem@cref}{{[theorem][1][2]2.1}{[1][1][]1}}

But if you uncomment the loading of thmtools, you get

newlabel{theorem}{{1.2}{1}}
newlabel{theorem@cref}{{[theorem][2][]1.2}{[1][1][]1}}
...
newlabel{othertheorem}{{2.1}{1}}
newlabel{othertheorem@cref}{{[theorem][1][]2.1}{[1][1][]1}}

As indicated by the fact that Simon Dispa's patch works, this seems that the inclusion of thmtools actually forcibly bypassed the code placed into cleveref that were designed to handle precisely the situation of sibling counters and checking for parents.

Suggestions:

  • Switch to using either amsthm or ntheorem, and drop thmtools.
  • Alternatively, send the maintainers of thmtools a note (maybe pointing to this answer). Something in their package is screwing things up, but I don't have the time to track it down now.

Updated Workaround

Below is a simpler workaround for your issue.

Use

documentclass{article}
usepackage{thmtools}
usepackage{cleveref}

declaretheorem[sibling=subsection]{theorem}

counterwithin{theorem}{section} %%% <---- NEW LINE

begin{document}
...

Brief explanation: with the LaTeX native newtheorem defined in latex.ltx, as well as the implementations in ntheorem and in amsthm, the above would not work. The reason is that their when you declare a sibling counter, the underlying counter used for the environment is the sibling counter. So newtheorem{theorem}[subsection]{Theorem} never creates a new theorem, it just internally marks that theorem should use subsection as the underlying counter.

The implementation in thmtools is different. To support autoref when you declare a sibling counter, what it does is create an alias for the sibling counter.

The neat thing that happens with this definition is that, for all intents and purposes it appears that there is a counter called theorem. It is just that underneath it the counter register is the same as that used for subsection. So that when you call stepcounter{theorem}, you will see that the subsection counter also goes up by one. And you can also do things like arabic{theorem} to show the arabic numeral representation of the subsection counter.

What thmtools doesn't do, however, is re-register the counter into the reset list of its sibling. Functionally for the purpose of resetting the counter, this is inconsequential. Registering both theorem and subsection into the reset list for section would just mean that when section is stepped, first subsection will reset to zero, then theorem (which is really subsection) will again reset to zero. So there's not much point of registering both.

However, from the point of view of cleveref, which reads the reset list to determine which counters are reset by which others, this makes a difference. Even though theorem is secretly subsection and hence is reset by section, it is not "obviously so" when looking at the reset list!

If would be nice if thmtools can be updated to help out cleveref and other packages that read the reset list by doing this registration. But since that is not the case, you can do it yourself. As mentioned before, theorem behaves pretty much like a normal counter. So you can just go ahead and call counterwithin. In terms of actually resetting the counter, it makes no impact. But now cleveref can see the parent-child relation.

Correct answer by Willie Wong on June 20, 2021

UPDATED

This patch will solve the issue as presented in the MWE using declaretheorem[sibling=subsection]{theorem} by adding an ordering reference number.

Previously, Theorem 1.2 was referenced to as Theorem #2 of nothing and Theorem 2.1 as Theorem #1 of nothing. Naturally, they were arranged in the wrong order, with second in the first place.

Adding the ordering reference number, now the reference to Theorem 1.2 is Theorem #2 of 1 and to Theorem 2.1 Theorem #1 of 2, so the sort order is correct.

z

documentclass{article}

usepackage{thmtools}
usepackage{cleveref}

declaretheorem[sibling=subsection]{theorem}

%%%************************************************* added
makeatletter
defrefstepcounter@noarg#1{% 
    cref@old@refstepcounter{#1}%
    cref@constructprefix{#1}{cref@result}%
    @ifundefined{cref@#1@alias}%
    {def@tempa{#1}}%
    {def@tempa{csname cref@#1@aliasendcsname}}%
    ifxcref@result@empty%
    defcref@result{thesection}%
    fi%
    protected@edefcref@currentlabel{%
        [@tempa][arabic{#1}][cref@result]%
        csname p@#1endcsnamecsname the#1endcsname}}%
makeatother
%%%*************************************************


begin{document}
    
    section{}
    begin{theorem}
        Blah.
    end{theorem}
    
    begin{theorem}label{theorem}
        Blah.
    end{theorem}
    
    section{}
    begin{theorem}label{othertheorem}
        Blah.
    end{theorem}
    
    Cref{theorem,othertheorem}.
    
end{document}

Answered by Simon Dispa on June 20, 2021

Workaround: make subsection track theorem, instead of the other way around.

documentclass{article}

usepackage{thmtools}
usepackage{cleveref}

letoriginalsubsectionsubsection
renewcommandsubsection[1]{%
        setcounter{subsection}{value{theorem}}%
        stepcounter{theorem}%
        originalsubsection{#1}}

declaretheorem[parent=section]{theorem}

begin{document}

section{}
begin{theorem}
Blah.
end{theorem}

subsection{Blank}

begin{theorem}label{theorem}
Blah.
end{theorem}


section{Test}
begin{theorem}label{othertheorem}
Blah.
end{theorem}

Cref{theorem,othertheorem}.

end{document}

enter image description here

Brief diagnosis:

thmtools+cleveref writes stuff like

newlabel{othertheorem@cref}{{[theorem][1][2]2.1}{[1][1][]1}}

into the aux field. Inside the second set of braces are instructions on how cleveref is supposed to interpet the label. First bracket is the counter type (usually counter name, but with thmtools / amsthm / ntheorem it tracks the name of the environment instead, since they frequently share the same counter), the second bracket is the counter value, and the third is the parent counter value. If you use thmtools to declare a sibling counter, and the counter is not one of the theorem counters, somehow the parent/numberwithin information is lost.

There may be a way to fix this by patching cleveref and/or amsthm/thmtools. But the above workaround is cheaper.

Answered by Willie Wong on June 20, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP