How to conditionally enable equation numbering for labeled equations?

In articles it is sometimes helpful to have unnumbered equations appear interlaced with numbered ones, the latter being referenced in the document.

How might I write the #set math.equation(numbering: (params) => output) rule so that only equations with reference tags are numbered? Is this something that would need language support to implement?

1 Like

I figured out a solution. Create the following function

#let equ(eq, id: none) = {
  let body = if type(id) == none {eq} else if type(id) == label [#eq #id] else [#eq <#id>]
  let numbering = if type(id) != none { "(1)" } else { none }
  set math.equation(numbering: numbering)
  body
}

Then you can control the showing of labels by setting the argument id. For example,

#lorem(50) As shown in @eq:eq1,
#equ($
y = a x + b
$, id: <eq:eq1>)
#lorem(100)
#equ($
E = m c^2
$)
#lorem(50)

image

Later I will try to figure out how to achieve this without using a custom function. But such a function is helpful. You can avoid the math equation breaking paragraphs with this

#let equ(eq, id: none) = {
  let body = if id == none {eq} else if type(id) == label [#eq #id] else [#eq #label(id)]
  let numbering = if id != none { "(1)" } else { none }
  set math.equation(numbering: numbering)
  linebreak()
  box(body, width: 1fr)
  linebreak()
}

In this case, display equations are really “in” the paragraph, not “between” two paragraphs.

And here is another solution without the help of a equ function. Put the following code before your document’s main body.

#show: body => {
  for elem in body.children {
    if elem.func() == math.equation and elem.block {
      let numbering = if "label" in elem.fields().keys() { "(1)" } else { none }
      set math.equation(numbering: numbering)
      elem
    } else {
      elem
    }
  }
}

And you can also append some other settings like this comment.

In this case, the following script

#lorem(30)
$ y = a x + b $ <eq:eq2>
#lorem(19)
@eq:eq2 #lorem(20)
$ E = m c^2 $
#lorem(10)

will result in

image

2 Likes

Thank you very much for the extensive options. I particularly like avoiding the custom function.

Hey @Christopher_Marcotte ! I’ve updated your post title to be in accordance with our question title guidelines: How to post in the Questions category

Remember that your title should be a question you’d ask to a friend about Typst. :wink:

1 Like

Hey @Aegon, quick tip! The syntax <#id> produces a label literally containing #id (that is, a hash, the letter “i”, and the letter “d”), not a label with the contents of the variable named id. Rather, you should write label(id) (use the label constructor) if you’d like to construct a label from a dynamic string. Hope this helps in the future!

Thanks! So the corrected version should be

#let equ(eq, id: none) = {
  let body = if id == none {eq} else if type(id) == label [#eq #id] else [#eq #label(id)]
  let numbering = if id != none { "(1)" } else { none }
  set math.equation(numbering: numbering)
  linebreak()
  box(body, width: 1fr)
  linebreak()
}

Right?

Yes (assuming id would always be a string in the last case).

@Christopher_Marcotte Unfortunately, this solution cannot work on equations in lists, enums, etc. To solve this, it is still necessary to define a function which applies the for-loop to its body argument.

Perhaps we can achieve this by using show once the non-recursive show-set is ready.

You may be interested in this package

1 Like

This appears to precisely solve the issue, very elegantly. Amazing, thank you!

#set math.equation(numbering: "(1)")
#show math.equation: it => {
  if not it.has("label") {
    let fields = it.fields()
    fields.remove("body")
    fields.numbering = none
    return [#counter(math.equation).update(v => v - 1)#math.equation(..fields, it.body)<math-equation-without-label>]
  }
  return it
}

$ x + y $

$ x + y + z $ <with-label>

82ae3879ab4cb384318bc01a3e883fc6

1 Like