I took a look at my rdfref package, which wasn't easy, as I haven't written any documentation and there were also no comments in the source code. So I improved the situation, fixed some bugs, wrote some comments, but the documentation is still to be done.
rdfref
is inspired with RDF, as name suggests. The idea is that every referencing command (\rdflabel
, \rdfref
, \rdfpageref
) add some properties for a current subject in the form of
subject attribute object
depending on the type of subject. The type of subject is based either on part of label before colon (so for \rdflabel{sec:hello} is the type
sec`), or on containing environment:
\begin{lem}\rdflabel{thm:lem1}
\partOf{thm:thr1}
A lemma. Reference to theorem\ref{thm:euclid}
\end{lem}
In this case the subject type is lem
. \partOf
is custom command which states that this lemma is part of theorem thm:thr1
.
Referencing commands defines another type of subject, so called blank node, so it is possible to search for such nodes and do some manipulation with them.
It is possible to specify added attributes for given subject type with \AddRdfType
command:
\AddRdfType{lem}{%
\AddProperty{rdf:type}{thm:lemma}
\AddPropertyEx{doc:pageNo}{\thepage}
\AddPropertyEx{rdfs:label}{\@currentlabel\ \@currentlabelname}
}
Properties are added with \AddProperty
and \AddPropertyEx
commands.
Some more properties are saved automatically by \rdflabel
: doc:hasParent
and doc:envir
. All properties are saved in the aux
file. If you search this file for thm:lem1
label, you will find this:
\LoadTriple {thm:lem1}{doc:hasParent}{sec:first}
\LoadTriple {thm:lem1}{doc:envir}{lem}
\LoadTriple {thm:lem1}{rdf:type}{thm:lemma}
\LoadTriple {thm:lem1}{doc:pageNo}{1}
\LoadTriple {thm:lem1}{rdfs:label}{1\ }
\LoadTriple {thm:lem1}{doc:partOf}{thm:thr1}
you can get value of any property with \GetPropery{subject}{property}
, so \GetProperty{thm:lem1}{doc:pageNo}
will print 1
.
Important property is rdf:type
, which all subjects should have assigned. The value of rdf:type
should be subject of its own, with rdf:type
set to rdfs:Class
and some descriptive rdfs:label
. It is also possible to define type hierarchy with rdfs:subClassOf
. You need to set these properties inside \WithObject
command, as these properties must be assigned only once. (\AddRdfType
saves properties every time \rdflabel
is called):
\WithObject{thm:lemma}{%
\AddProperty{rdfs:label}{Lemma}
\AddProperty{rdf:type}{rdfs:Class}
\AddProperty{rdfs:subClassOf}{thm:theorem}
}
thm:theorem
class is defined as parent class in this example.
All these properties should be declared in so called ontology file, like this one theorems-ontology.tex
for theorems:
\newcommand\partOf[1]{%
\AddPropertyEx{doc:partOf}{#1}
}
\AddRdfType{Theorem}{%
\AddProperty{rdf:type}{thm:theorem}
\AddPropertyEx{doc:pageNo}{\thepage}
\AddProperty{rdfs:label}{\@currentlabel\ \@currentlabelname}
}
\AddRdfType{thr}{%
\AddProperty{rdf:type}{thm:theorem}
\AddPropertyEx{doc:pageNo}{\thepage}
\AddPropertyEx{rdfs:label}{\@currentlabel\ \@currentlabelname}
}
\AddRdfType{prop}{%
\AddProperty{rdf:type}{thm:proposition}
\AddPropertyEx{doc:pageNo}{\thepage}
\AddPropertyEx{rdfs:label}{\@currentlabel\ \@currentlabelname}
}
\AddRdfType{lem}{%
\AddProperty{rdf:type}{thm:lemma}
\AddPropertyEx{doc:pageNo}{\thepage}
\AddPropertyEx{rdfs:label}{\@currentlabel\ \@currentlabelname}
}
\AddRdfType{sec}{
\AddProperty{rdf:type}{sec:sectioning}
\AddPropertyEx{doc:pageNo}{\thepage}
\AddPropertyEx{rdfs:label}{\@currentlabel\ \@currentlabelname}
\AddPropertyEx{doc:label}{\@currentlabel}
}
\AddRdfType{equation}{%
\AddProperty{rdf:type}{eq:equation}
\AddPropertyEx{doc:pageNo}{\thepage}
\AddProperty{rdfs:label}{\@currentlabel\ \@currentlabelname}
}
%Add properties for classes
\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}
\AddProperty{rdfs:subClassOf}{thm:theorem}
}
\WithObject{thm:lemma}{%
\AddProperty{rdfs:label}{Lemma}
\AddProperty{rdf:type}{rdfs:Class}
\AddProperty{rdfs:subClassOf}{thm:theorem}
}
\WithObject{eq:equation}{%
\AddProperty{rdfs:label}{Equation}
\AddProperty{rdf:type}{rdfs:Class}
\AddProperty{rdfs:subClassOf}{thm:theorem}
}
\WithObject{rdfs:Class}{%
\AddProperty{rdfs:label}{Class}
}
now we can look at the document preamble:
\documentclass{article}
\usepackage[utf8]{inputenc}
\usepackage[]{tgschola}
\usepackage[T1]{fontenc}
\usepackage{hyperref}
\usepackage[]{rdfref-user,rdfref-query,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}
thmtools
package is used to define new theorem like environments lem
, prop
and thr
. Now include the ontology and redefine \ref
command to \rdfref
, just to show that it works:
\begin{document}
\inputontology{theorem-ontology.tex}
\let\ref\rdfref
now some sample text with theorems (I am not a mathematician so it is pure nonsense, I just want to show some possibilities):
\section{Hello world}\rdflabel{sec:first}
\lipsum[1-2]
\begin{lem}\rdflabel{thm:lem1}
\partOf{thm:thr1}
A lemma. Reference to theorem \ref{thm:euclid}
\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}
As it was said in theorem \ref{thm:euclid}, we see that
In our ontology, we defined also sec
type, so with \section{Hello world}\rdflabel{sec:first}
subject of rdf:type
sec:sectioning
is added.
Now it is possible to make some fun with rdfref-query
package. This package enables us to easily query our database. If we want to search for all theorem like subjects, we can use the fact that all of them have rdf:type
property which is sub class of thm:theorem
class. We can make simple command which looks for all subjects with rdfs:subClassOf
= thm:theorem
property and make list which can be used in the future:
\newcommand\buildtypelist[2]{%
\listadd#1{#2} % add searched
\Bind{?x}{rdfs:subClassOf}{#2}{%
% only direct children of #2 are taken into account
\listxadd#1{\GetVal{?x}}% must be global and expanded
}%
}
%
\buildtypelist\theoremlist{thm:theorem}
\Bind
command is really useful. It takes four parameters, some of them may be unknown values (starting with ?
) which are bind during search. First three parameters are subject
, attribute
and object
, fourth is a function body which is executed for each found value.
So in this case, all subjects which have rdfs:subClassOf
equal to thm:theorem
are processed. etoolbox
's command \listxadd
is used to build a list. To get a value of bound variable, \GetVal
command must be used.
With the list built, we can process all theorems with etoolbox's list looping commands:
\newcommand\MakeLabel[1]{%
\Bind{#1}{rdf:type}{?type}{% get type of parent
\textbf{\GetValProperty{?type}{rdfs:label}} % parent type label
}%
% now clickable reference to parent
\hyperref[\GetVal{#1}]{\GetValProperty{#1}{rdfs:label}}%
}
\renewcommand\do[1]{%
\Bind{?obj}{rdf:type}{#1}{%
% Prin basic info about the object
\par Object type:
\textbf{\GetValProperty{#1}{rdfs:label}}, % #1 is theorem type label
% now clickable reference to label of current object
value:\textbf{\hyperref[\GetVal{?obj}]{\GetValProperty{?obj}{rdfs:label}}},
% and just label
label: [\GetVal{?obj}].
% Print info about parent object:
\Bind{?obj}{doc:hasParent}{?par}{% parent label
\par \hspace{2em}Parent object:%
\MakeLabel{?par}%
}%
\def\refinfo{\par\hspace{2em} Referenced by:\par\hspace{3em}}
\Bind{?ref}{doc:refersTo}{?obj}{%
\refinfo\def\refinfo{\par\hspace{3em}}%
\GetValProperty{?ref}{rdfs:label} in
\Bind{?ref}{doc:hasParent}{?par}{%
\MakeLabel{?par}%
}, p. \hyperpage{\GetValProperty{?ref}{doc:pageNo}};%
}%
}}
\dolistloop\theoremlist
This is complex example, which loops over all theorem types. \Bind{?obj}{rdf:type}{#1}
will loop over all theorems of given type. New command is introduced \GetValProperty
, which get property of bound variable. Helper macro \MakeLabel
is used to print label for object. It is used for print info about parent subject (\Bind{?obj}{doc:hasParent}{?par}
). References to given theorem are searched with \Bind{?ref}{doc:refersTo}{?obj}
.
Full sample file can be found on rdfref page. Resulting information:
Now we can move to the graph:
First define some auxilary macros:
% Now export a theorem graph
% Make expandable label
\newcommand\MakePlainLabel[1]{%
\GetProperty{\GetProperty{#1}{rdf:type}}{rdfs:label}
\GetProperty{#1}{rdfs:label}
}
we will use \MakePlainLabel
to make labels for nodes
% Make list of used nodes, every node should be added only once
% #1 list csname #2 node prefix #3 subject
\newcommand\AddNode[3]{%
\ifcsdef{node@lbl@#2@#3}{}{%
\listxadd#1{#3}
\csgdef{node@lbl@#2@#3}{\MakePlainLabel{#3}}
}%
}
we need to add label only once, so we define a list and add each subject only once
% replace colons with underscores
\def\coltoun#1:#2;{#1_#2}% graphwiz doesn't support colon in names
\def\graphrelations{}
% connect nodes
% #1 source node #2 destination #3 graphviz style
\newcommand\AddRelation[3]{%
\edef\tempa{#1}%
\edef\tempb{#2}%
% add node label. we can call \AddNode for node multiple times,
% node is beeing added only once
\AddNode\mynodes{myn}{#1}%
\AddNode\mynodes{myn}{#2}%
% store relations in a macro
\xdef\graphrelations{%
\graphrelations \expandafter\coltoun\tempa; -> \expandafter\coltoun\tempb; [#3];^^J
}%
}
\AddRelation
will add nodes labels, if the weren't used yet, and defines relation between the two.
Now we can make the actual graph.
Idea is following: loop over all theorems and connect them with their parent objects. For theorems connected with \partOf
command, use doc:partOf
attribute, otherwise use doc:hasParent
. Then loop over all references to given theorem, get parent objects and connect them to the theorem with dotted line.
\renewcommand\do[1]{%
\Bind{?obj}{rdf:type}{#1}{%
% select parent node. for lemmas etc, which use doc:partOf property,
% use that, otherwise use doc:has:parent
\IfProperty{\GetVal{?obj}}{doc:partOf}{%
\edef\parnode{\GetValProperty{?obj}{doc:partOf}}
}{
\edef\parnode{\GetValProperty{?obj}{doc:hasParent}}
}
\AddRelation{\GetVal{?obj}}{\parnode}{}
\Bind{?ref}{doc:refersTo}{?obj}{%
% select references to a theorem and print parent subject
\def\parnode{\GetValProperty{?ref}{doc:hasParent}}
\AddRelation{\parnode}{\GetVal{?obj}}{style=dotted}
}
}
}
% again loop over al theorems
\dolistloop\theoremlist
now we need some macros to store
graph in graphviz format to a file:
% now prepare output file for the graphviz output
\newwrite\graphwrite
% macros for opening and writing to the graph file
\newcommand\graphopen[1]{%
\immediate\openout\graphwrite=#1%
}
\newcommand\graphout[1]{\immediate\write\graphwrite{#1}}
\graphopen{\jobname.dot} % graphviz file name will be the same
% as input file name with .dot extension
\graphout{digraph hello\@charlb} % save graph header
% save labels
\renewcommand\do[1]{%
\graphout{\coltoun#1; [label="\MakePlainLabel{#1}"]}
}
\dolistloop\mynodes
% save graph relations
\graphout{\graphrelations}
\graphout{\@charrb} % save footer
after document processing with LaTeX, file filename.dot
is saved:
digraph hello{
thm_thr1 [label="Theorem 1\ "]
sec_first [label="Sectioning 1\ Hello world "]
thm_euclid [label="Theorem 2\ Euclid "]
thm_lem1 [label="Lemma 1\ "]
thm_prop1 [label="Proposition 1\ "]
eq_1 [label="Equation 1\ "]
thm_thr1 -> sec_first [];
thm_euclid -> sec_first [];
thm_lem1 -> thm_euclid [style=dotted];
sec_first -> thm_euclid [style=dotted];
thm_prop1 -> thm_thr1 [];
thm_lem1 -> thm_thr1 [];
eq_1 -> thm_euclid [];
}
you can compile it with graphviz:
dot -Tpng filename.dot -o filename.png
and the resulting graph:
showkeys
andshowlabels
, but these packages are not offering such complex feature as requested.