Cross referencing in LaTeX
uses quite simple schema - for every label it stores value of macro \@currentlabel
and the page number in the aux file. There are packages, which extends this scheme, like titleref
or zref
, but it is still just label and some properties, which are then accessed with some form of \ref
command.
What would be useful in this case, is something like RDF so one can construct complex graph of objects, their properties and relationships, like
Theorem Lorem is defined in the Section Ipsum on page 4 and it has
Definition foo on page 5 and Example boo on page 6, is referenced on
pages 3,7,89 etc...
In this model every label
(object) can have any number of properties, which can be values, or other objects, which gives us directed graph of objects, properties and subjects. objects and properties are in form of prefix:name
,
Now there is some simple rdf
framework for LaTeX
First we define some core functions for saving and loading properties
rdfref-core.sty
\ProvidesPackage{rdfref-core}
\RequirePackage{etoolbox}
\def\Nil{}%
\def\First#1#2{#1}
\def\Second#1#2{#2}
\def\parsecolon#1:#2\relax{\ifx#2\Nil\Nil\else#1\fi}%
\newcommand\RdfPrefix[1]{%
\parsecolon#1:\Nil\relax%
}
\newcommand\AddTriple[3]{%
\edef\rdf@object{#3}
\immediate\write\@auxout{\noexpand\LoadTriple{#1}{#2}{\rdf@object}}
}
\newcommand\SetProperty[3]{%
\protected@csxdef{rdf@#1@#2}{#3}%
}
\newcommand\GetProperty[2]{%
\ifcsdef{rdf@#1@#2}{%
\csuse{rdf@#1@#2}%
}{%
\PropertyNotDef{#1}{#2}%
}%
}
\newcommand\PropertyNotDef[2]{%
???%
%\typeout{rdf warning: subject #1 has no property #2}%
}
\newcommand\SubjectListAdd[2]{%
\listcsgadd{sbjlst@#1}{#2}
}
\newcommand\MapSubjectList[2]{%
\def\do##1{#2}%
\dolistcsloop{sbjlst@#1}%
}
\newcommand\PropertyListAdd[3]{%
\listcsgadd{prprtlst@#2}{{#1}{#3}}
}
\newcommand\MapPropertyList[2]{%
\def\do##1{#2}%
\dolistcsloop{prprtlst@#1}%
}
\newcommand\LoadTriple[3]{%
\SetProperty{#1}{#2}{#3}
\SubjectListAdd{#1}{#2}
\PropertyListAdd{#1}{#2}{#3}
}
Two useful functions are \MapPropertyList
, which lets you to loop over all objects and subjects with given property and \MapSubjectList
for loop over properties of given subject.
Now some helper functions, which emulates somehow latex cross-referencing system rdfref-user.sty
:
\ProvidesPackage{rdfref-user}
\RequirePackage{rdfref-core,nameref}
\newwrite\exportfile
\def\RootObject{_:doc}
\def\CurrentObject{\RootObject}
\newcommand\AddRdfType[2]{%
\def\do##1{\csgdef{rdf:type@##1}{\noexpand\unexpanded{#2}}}
%\csgdef{rdf:type@#1}{\unexpanded{#2}}
\docsvlist{#1}
}
\newcommand\AddProperty[2]{%
\AddTriple{\CurrentObject}{#1}{#2}
}
\newcommand\WithObject[2]{%
\begingroup%
\def\CurrentObject{#1}%
\def\run{#2}
\run%
\endgroup%
}
\newcommand\rdflabel[1]{%
\AddTriple{#1}{doc:hasParent}{\CurrentObject}%
\AddTriple{#1}{doc:envir}{\@currenvir}%
\def\CurrentObject{#1}%
\ifcsdef{rdf:type@\GuessRdfType{#1}}%
{\csuse{rdf:type@\GuessRdfType{#1}}}%
{\typeout{Package rdfref error: unknown type of reference #1 :\GuessRdfType{#1}:}}%
\label{#1}
}
\newcommand\GuessRdfType[1]{%
% We first try if current environment is defined as type, if not, then guess type from the parameter
\ifcsdef{rdf:type@\@currenvir}{\@currenvir}{%
\ifx\RdfPrefix{#1}\Nil\Nil\else%
\RdfPrefix{#1}%
\fi%
}%
}
\newcommand\inputontology[1]{%
\makeatletter
\input{#1}
\makeatother
}
command \rdflabel
constructs LaTeX
label, so you can use normal constructs like \ref
and \pageref
and creates object with some default values and then call constructor which stores properties which are defined for object of given type. Type is deducted from object's surrounding environment
, if it doesn't have set type, it is deducted from its prefix.
These constructors are defined in ontologies, which are normal TeX
files with macro definitions, but which must be loaded after \begin{document}
, because they need to write to the aux
file.
theorem-ontology.tex
\newcommand\partOf[1]{%
\AddProperty{doc:partOf}{#1}
}
\AddRdfType{Theorem}{%
\AddProperty{rdf:type}{thm:theorem}
\AddProperty{doc:pageNo}{\thepage}
\AddProperty{rdfs:label}{\@currentlabel\ \@currentlabelname}
}
\AddRdfType{thr}{%
\AddProperty{rdf:type}{thm:theorem}
\AddProperty{doc:pageNo}{\thepage}
\AddProperty{rdfs:label}{\@currentlabel\ \@currentlabelname}
}
\AddRdfType{prop}{%
\AddProperty{rdf:type}{thm:proposition}
\AddProperty{doc:pageNo}{\thepage}
\AddProperty{rdfs:label}{\@currentlabel\ \@currentlabelname}
}
\AddRdfType{lem}{%
\AddProperty{rdf:type}{thm:lemma}
\AddProperty{doc:pageNo}{\thepage}
\AddProperty{rdfs:label}{\@currentlabel\ \@currentlabelname}
}
\AddRdfType{sec}{
\AddProperty{rdf:type}{sec:sectioning}
\AddProperty{doc:pageNo}{\thepage}
\AddProperty{rdfs:label}{\@currentlabel\ \@currentlabelname}
\AddProperty{doc:label}{\@currentlabel}
}
\AddRdfType{equation}{%
\AddProperty{rdf:type}{eq:equation}
\AddProperty{doc:pageNo}{\thepage}
\AddProperty{rdfs:label}{\@currentlabel\ \@currentlabelname}
}
\WithObject{sec:sectioning}{%
\AddProperty{rdfs:label}{Sectioning}
\AddProperty{rdf:type}{rdfs:Class}
}
\WithObject{thm:theorem}{%
\AddProperty{rdfs:label}{Theorem}
\AddProperty{rdf:type}{rdfs:Class}
}
\WithObject{thm:proposition}{%
\AddProperty{rdfs:label}{Proposition}
\AddProperty{rdf:type}{rdfs:Class}
}
\WithObject{thm:lemma}{%
\AddProperty{rdfs:label}{Lemma}
\AddProperty{rdf:type}{rdfs:Class}
}
\WithObject{eq:equation}{%
\AddProperty{rdfs:label}{Equation}
\AddProperty{rdf:type}{rdfs:Class}
}
\WithObject{rdfs:Class}{%
\AddProperty{rdfs:label}{Class}
}
you can see that we added constructors for some environments and prefixes. important property is rdf:type
, which can be used for example if you want to loop over all theorems or equations. there are also properties doc:pageNo
and rdfs:label
, which are more or less traditional LaTeX
cross-referencing properties. For every rdf:object
there is also \WithObject
macro, which set textual labels (rdfs:label
) for these types.
there is also command \partOf
, which declares current object as part of another object. It can be used in examples or definitions, to declare that they are part of some theorem. I do really know nothing about mathematical theorems, so this is really just an simple example, not anything useful. If you have idea about some useful properties, they can be easily added.
Now some sample file:
\documentclass{article}
\usepackage[utf8]{inputenc}
\usepackage[]{tgschola}
\usepackage[T1]{fontenc}
%\catcode`\_=12
\usepackage[]{rdfref-user,lipsum,nameref,cleveref}
\usepackage{amsthm,thmtools,xstring}
\declaretheorem[name=Lemma,Refname={Lemma,Lemmas}]{lem}
\declaretheorem[name=Proposition,Refname={Proposition,Proposition}]{prop}
\declaretheorem[name=Theorem,Refname={Theorem,Theorems}]{thr}
\begin{document}
\inputontology{theorem-ontology.tex}
\section{Hello world}\rdflabel{sec:first}
\lipsum[1-2]
\begin{lem}\rdflabel{thm:lem1}
\partOf{thm:thr1}
A lemma.
\end{lem}
\begin{prop}
\rdflabel{thm:prop1}
\partOf{thm:thr1}
A proposition.
%\renewcommand{\theenumi}{\thecor.\arabic{enumi}}
\begin{enumerate}
\item An item
\item Another one
\end{enumerate}
\end{prop}
\begin{thr}\rdflabel{thm:thr1}
A theorem.
\end{thr}
\begin{thr}[Euclid]
\rdflabel{thm:euclid}%
For every prime $p$, there is a prime $p’>p$.
In particular, the list of primes,
\begin{equation}\rdflabel{eq:1}
2,3,5,7,\dots
\end{equation}
is infinite.
\end{thr}
\end{document}
And if you want to export some graph, try this macro (Not much nice, but it shows some ways, how you can work with your properties graph):
\newcommand\ExportGraphviz[2]{%
digraph G\{
\forcsvlist{\listadd\typelist}{#1}
%\forcsvlist{\listadd\propertylist}{#2}
\def\worker##1##2{%
%"\ObjectLabel" -> \GetProperty{\First##1}
\edef\ppp{\GetProperty{##1}{##2}}
\typeout{Worker: ##1: ##1 : \ppp}
\IfSubStr{\ppp}{???}{}{%
"\ObjectLabel" -> %
\IfSubStr{\ppp}{:}{"\GetProperty{\GetProperty{\ppp}{rdf:type}}{rdfs:label}: \GetProperty{\ppp}{rdfs:label}"}{"\ppp"} %
%\GetProperty{##1}{##2}
[label="##2"];\\%
}
}
\MapPropertyList{rdf:type}{%
\xifinlist{\Second##1}{\typelist}{%
\edef\ObjectLabel{\GetProperty{\Second##1}{rdfs:label}: \GetProperty{\First##1}{rdfs:label}}%
%\ObjectLabel \& \Second##1\\}
\bgroup%
%\MapObjectList{\First##1}{}
\forcsvlist{\worker{\First##1}}{#2}%
\egroup
}%
{}%
}
\}
}
Because of some weird errors, I cannot do version which write to the file, so graphviz specification is simply printed to the document (really ugly, I know). It can be used in following way:
\ExportGraphviz{sec:sectioning,thm:theorem,eq:equation,thm:proposition,thm:proposition,thm:lemma}{doc:pageNo,doc:partOf,doc:hasParent}
First parameter is list of objects with given rdf:type
which should be the exported, the second is list of properties. For some reason, on first run, when there is no aux file, it gives some errors, but after that, it should be OK
Resulting graph:

Also, it is easy using \MapPropertyList
and \MapSubjectList
to export data to some rdf
serialization format, for example turtle and import them to some specialized rdf
database, or visualization tool, and do many interesting things with them.
\ref
macro in a macro of your own that writes the context of the reference to an external file (or saves it in an internal list). That would give the data "A uses B". Parsing/sorting that list would tell you also "B used by A".TeX
to write one, which I would if I needed it. But I don't, and don't have the time. Maybe one of the wizards who watch SE regularly can do it quickly.reftex
foremacs
is great for tracking references across a large project provided you set up which file is the 'parent' and which ones are the 'children'. It comes bundled as part ofAUCTeX
.