SageTeX: 1000sep for sage-calculated number (siunitx?)

The problem

The \sage and \sagestr macros do many things at once, some of which are not “expandable” (actually, it is \ST@sage which does most of the work). This is exactly the same problem as with \ref, which is directly used by \ST@sage. Fortunately, there are ways to design an expandable interface in such cases; this is what packages such as refcount and zref do. The idea is simple:

  1. In expansion-only contexts, you use an “expandable” command1 to retrieve tokens associated to a reference which we'll call foobar here. This command may expand to various sequences of tokens based on test outcomes, but can't print error messages or perform assignments (except with LuaTeX, these things can't be done in expansion-only contexts). If the foobar reference you asked for is defined, the command expands to the tokens associated with this reference (in your case, that would be the result of a Sage computation).

  2. In a place where you can do more than only-expanding-tokens, you use a second command to tell LaTeX that you have used reference foobar. Then, if LaTeX finds that reference foobar isn't defined (because for instance Sage hasn't been run yet in our case), it will remember and warn you at the end of the run that some references were undefined and you need to rerun something (here, Sage).

In our case, there is a third command needed before these two: the one that associates a macro of your choice—let's call it \macro—to a Sage expression so that the expression will be written to the .sagetex.sage file for later processing by Sage. Then you can use a call such as \estGet{\macro} to recover in an expandable manner the value of the expression computed by Sage. Here, \estGet is a macro from package expsagetex, which I wrote for this answer and makes it very easy to obtain the desired result.

A solution with expsagetex

expsagetex is a LaTeX package available here. It is a layer on top of sagetex that allows one to retrieve results from Sage in an expandable manner. I'll explain how to use it below, but you may want to begin with the Quick start section from expsagetex.pdf (source code here). This is a file with a tutorial, many examples and explanations.

Using expsagetex, your problem can be solved as follows. Both sample files can be compiled with:

pdflatex document.tex
sage document.sagetex.sage
pdflatex document.tex

(alternatively, you may use lualatex or xelatex).

Without automatic line wrapping

\documentclass{article}
\usepackage{expsagetex}
\usepackage{siunitx}
\sisetup{group-separator = \,}

\setlength{\parindent}{0pt}

\begin{document}

With \verb|\estRecordFormatted|:\smallskip

\estRecordFormatted{\myIntRef}{2**233}%
\num{\estGet[-1]{\myIntRef}}%
\estRefUsed{\myIntRef}

\medskip
With \verb|\estRecordStr|:\smallskip

\estRecordFormatted{\myIntRefII}{str(2**233)}%
\num{\estGet[-1]{\myIntRefII}}%
\estRefUsed{\myIntRefII}

\end{document}

Without automatic line wrapping

In case you want to reuse your Python variable x defined by:

\begin{sageblock}
x = 2**333
\end{sageblock}

you can of course do so by replacing, e.g., \estRecordFormatted{\myIntRef}{2**233} with \estRecordFormatted{\myIntRef}{x}. This is because in the context where this matters, both 2**333 and x are Python expressions that evaluate to the same object: the integer you want to print.

With automatic line wrapping

In case you want to have automatic line wrapping for the number, this can be done by not using siunitx and post-processing the number obtained from expsagetex in order to insert horizontal glue in the appropriate places. Here is a way to do so with expl3:

\documentclass{article}
\usepackage{expsagetex}
\usepackage{xparse}

\ExplSyntaxOn

\regex_const:Nn \c_cis_regex { ( \cB\{ \c[LO]. \cE\} ){3} }
\tl_new:N \l_cis_tmpa_tl

\cs_generate_variant:Nn \tl_reverse_items:n { V }
\prg_generate_conditional_variant:Nnn \tl_if_eq:nn { Vx } { TF }

% #1: separator
% #2: “number”
\cs_new_protected:Npn \cis_seperate_thousands:nn #1#2
  {
    \tl_set:Nx \l_cis_tmpa_tl { \tl_reverse_items:n {#2} }
    \tl_set:Nn \l_tmpa_tl {#1}
    \regex_replace_all:NnN \c_cis_regex
      { \0 \cB\{ \u{\l_tmpa_tl} \cE\} } \l_cis_tmpa_tl
    \tl_set:Nx \l_cis_tmpa_tl { \tl_reverse_items:V \l_cis_tmpa_tl }

    \tl_if_eq:VxTF \c_space_tl { \tl_head:N \l_cis_tmpa_tl }
      { \tl_tail:N \l_cis_tmpa_tl }
      { \tl_use:N \l_cis_tmpa_tl }
  }

\cs_generate_variant:Nn \cis_seperate_thousands:nn { nx }

% The default separator (\,) is unbreakable.
\NewDocumentCommand \separateThousands { O{\,} m }
  { \cis_seperate_thousands:nx {#1} {#2} }

\NewDocumentCommand \thinbreakablespace { }
  { \hspace{.16667em plus 0.01em} }

\ExplSyntaxOff

\setlength{\parindent}{0pt}

\begin{document}

With \verb|\estRecordFormatted|:\smallskip

\estRecordFormatted{\myIntRef}{2**233}%
\separateThousands[\thinbreakablespace]{\estGet[-1]{\myIntRef}}%
\estRefUsed{\myIntRef}

\medskip
With \verb|\estRecordStr|:\smallskip

\estRecordFormatted{\myIntRefII}{str(2**233)}%
\separateThousands[\thinbreakablespace]{\estGet[-1]{\myIntRefII}}%
\estRefUsed{\myIntRefII}

\end{document}

With automatic line wrapping

Here too, you can use the x variable as input exactly in the same way as explained above.

Using expsagetex

Here are explanations on how to use expsagetex that complement the currently available documentation (expsagetex.pdf, LaTeX source).

  • The \est*Record* functions (\est[GA]RecordStr, \est[GA]RecordFormatted and \est[GA]RecordFloat) are all of the “third” kind of command described in the first part of this answer. Calling \estRecordStr{\macro}{sage expression} records sage expression in the .sagetex.sage file and associates the resulting output with \macro. After this, \macro will expand to an integer, but you don't need to know that. You'll need it to recover the value computed by Sage, by passing it to \estGet. You should also pass it to \estRefUsed (see below).

  • These \est*Record* functions assign \macro locally, except those with a G in their name, which assign it globally.

  • The \est*Record* functions with an A before Record in their name (A for “associative array”) accept two arguments instead of the single \macro argument. For instance, \estARecordStr{base}{\whatever-\you-\want}{sage expression} is equivalent to \estRecordStr{\res}{sage expression} where \res is the control sequence token obtained by expanding \csname base@\whatever-\you-\want\endcsname once.

  • \estRecordFormatted{\macro}{sage expression} is similar to \estRecordStr{\macro}{sage expression}, except that it stores latex(sage expression) instead of just sage expression in the .sagetex.sage file. This may introduce math-mode commands in the LaTeX code obtained from sagetex if the result of evaluating str(sage expression) in Sage contains things such as exponents, brackets, etc.

  • \estRecordFloat{\macro}{sage expression}{format} is for recording a floating point number in a particular format. For instance, if format is .10f, this will format the float as if you had issued "%.10f" % (sage expression,) in Python, i.e.: the result will be formatted with 10 decimal places and won't use exponent notation. If you need more flexibility, use \estRecordStr (if you need a percent sign for the Python code, you can use the \percent macro provided by sagetex or expl3's \c_percent_str constant).

  • \estGet is the command to retrieve a result in an expandable way. More precisely, \estGet[fallback]{\macro} ultimately expands to the result associated with \macro by one of the \est*Record* functions (the result is wrapped within \exp_not:n, aka \unexpanded). The first argument fallback is optional and used when the reference corresponding to \macro isn't defined yet (probably meaning that you need to run Sage on the .sagetex.sage file). This argument is particularly useful when you call \estGet in a place where the default, namely ??, would cause an error (e.g., inside the mandatory argument of siunitx's \num command).

  • \estRefUsed{\macro} is what you should do to indicate that your code uses the result associated to \macro. This will generate the appropriate warning if the reference isn't defined yet, otherwise it won't do anything at all. You should use it whenever you have \estGet[fallback]{\macro} somewhere in your document. Since \estRefUsed is not expandable, you will often call \estGet and \estRefUsed in close vicinity, but not exactly in the same place. For instance:

    \estRecordFormatted{\mymacro}{2**5}  % record the expression 2**5
    \edef\myresult{\estGet[0]{\mymacro}} % store result in \myresult
    \estRefUsed{\mymacro}                % warn if it's not in the .sout file yet
    \show\myresult                       % show result on terminal
    

    In this example, it would not be correct to use \estRecordFormatted or \estRefUsed inside the \edef.

Of these macros, only \estGet is expandable (as well as its expl3 sister \est_get:nnn). But this is the important one, the one you can use inside \edef, \write, \fpeval, \num, and many other places. For instance, if what it retrieves from Sage is of suitable type, you can use \estGet in the middle of an \fpeval calculation, in a counter or length (dimen or skip) register assignment, etc. You can even use it to compute the coordinates of many points in order to draw a plot with TikZ, pgfplots or any other TeX-based plotting framework. There are examples of all these things in expsagetex.pdf; have a look at its LaTeX source. The possibilities are endless!


Footnote

  1. In this answer, we say that a command is “expandable” if it does its job correctly when used inside \edef, i.e.: it expands to whatever it was designed to expand to, with no unwanted side effects. This property is often called “fully expandable”, but the terminology isn't really fixed. At least Heiko Oberdiek uses the term “expandable” with the same meaning as I am using here in some of his package manuals (IIRC), thus I feel relatively safe doing so. :-)

This is a little clunky, but it works for me:

\documentclass{report}
\usepackage{sagetex}
\usepackage{siunitx}
\sisetup{group-separator = \text{\,}}
\begin{document}

\begin{sageblock}
x=2^333
\end{sageblock}

\begin{sagesilent}
start="\\num{"
end="}"
\end{sagesilent}

Works: \sage{x}

Works, too: \sagestr{start+latex(x)+end}

\end{document}