diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml
index 1cab19eb..5a751301 100644
--- a/.github/workflows/CI.yml
+++ b/.github/workflows/CI.yml
@@ -25,11 +25,11 @@ jobs:
matrix:
gap-branch:
- master
+ - stable-4.13
- stable-4.12
- - stable-4.11
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- uses: gap-actions/setup-gap@v2
with:
GAP_PKGS_TO_BUILD: "io profiling AttributeScheduler grape datastructures orb digraphs NautyTracesInterface"
@@ -38,6 +38,9 @@ jobs:
- uses: gap-actions/build-pkg@v1
- uses: gap-actions/build-pkg-docs@v1
- uses: gap-actions/run-pkg-tests@v2
+ - uses: gap-actions/run-pkg-tests@v2
+ with:
+ only-needed: true
- uses: gap-actions/process-coverage@v2
- uses: codecov/codecov-action@v4
with:
@@ -51,7 +54,7 @@ jobs:
steps:
- run: sudo apt-get update
- run: sudo apt-get install sed texlive-latex-base texlive-latex-recommended texlive-latex-extra texlive-extra-utils texlive-fonts-recommended texlive-fonts-extra tex4ht
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- uses: gap-actions/setup-gap@v2
with:
GAP_PKGS_TO_BUILD: "io profiling AttributeScheduler grape datastructures orb digraphs NautyTracesInterface"
@@ -62,7 +65,7 @@ jobs:
with:
use-latex: 'true'
- name: 'Upload documentation'
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: manual
path: ./doc/manual.pdf
diff --git a/PackageInfo.g b/PackageInfo.g
index 409fd00f..bedb5e1d 100644
--- a/PackageInfo.g
+++ b/PackageInfo.g
@@ -66,6 +66,16 @@ Persons := [
PostalAddress := "--",
Place := "Aachen",
Institution := "Chair of Algebra and Representation Theory",
+ ),
+ rec(
+ IsAuthor := true,
+ IsMaintainer := true,
+ FirstNames := "Lukas",
+ LastName := "Schnelle",
+ Email := "lukas.schnelle1@rwth-aachen.de",
+ PostalAddress := "--",
+ Place := "Aachen",
+ Institution := "Chair of Algebra and Representation Theory",
),
rec(
LastName := "GAP Team",
@@ -112,7 +122,7 @@ PackageDoc := rec(
),
Dependencies := rec(
- GAP := ">= 4.11",
+ GAP := ">= 4.12",
NeededOtherPackages := [ [ "Grape", ">=4.8.2" ], [ "AttributeScheduler", ">=2018.08.03" ], ["Digraphs", ">=1.1.1"],[ "NautyTracesInterface", ">=0.2" ]],
SuggestedOtherPackages := [ [ "GAPDoc", ">= 1.6" ], ["AutoDoc", ">=2019.05.20"], [ "IO", ">=2.2" ]],
ExternalConditions := [ ],
diff --git a/doc/images/_Wrapper_Butterfly_Faithful_Monomorphism_Hexagon.pdf b/doc/images/_Wrapper_Butterfly_Faithful_Monomorphism_Hexagon.pdf
new file mode 100644
index 00000000..b2d0a87d
Binary files /dev/null and b/doc/images/_Wrapper_Butterfly_Faithful_Monomorphism_Hexagon.pdf differ
diff --git a/doc/images/_Wrapper_Butterfly_Faithful_Monomorphism_Hexagon.svg b/doc/images/_Wrapper_Butterfly_Faithful_Monomorphism_Hexagon.svg
new file mode 100644
index 00000000..5b8c940e
--- /dev/null
+++ b/doc/images/_Wrapper_Butterfly_Faithful_Monomorphism_Hexagon.svg
@@ -0,0 +1,428 @@
+
+
+
+
diff --git a/doc/tikz-files/Image_Butterfly_Faithful_Monomorphism_Hexagon.tex b/doc/tikz-files/Image_Butterfly_Faithful_Monomorphism_Hexagon.tex
new file mode 100644
index 00000000..db621e50
--- /dev/null
+++ b/doc/tikz-files/Image_Butterfly_Faithful_Monomorphism_Hexagon.tex
@@ -0,0 +1,319 @@
+\documentclass{standalone}
+
+\pagestyle{empty}
+% This document contains the TikZ-header for all our LaTeX-computations.
+% It especially contains all global graphic parameters.
+
+\usepackage{amsmath, amssymb, amsfonts} % Standard Math-stuff
+
+\usepackage{ifthen}
+
+\usepackage{tikz}
+\usetikzlibrary{calc}
+\usetikzlibrary{positioning}
+\usetikzlibrary{shapes}
+\usetikzlibrary{patterns}
+
+
+% Sometimes we want to implement different behaviour for the generated
+% HTML-pictures (for example, shading is not supported in HTML).
+% For that we define a macro to check whether we run the code with
+% htlatex. The code comes from
+% https://tex.stackexchange.com/questions/93852/what-is-the-correct-way-to-check-for-latex-pdflatex-and-html-in-the-same-latex
+\makeatletter
+\edef\texforht{TT\noexpand\fi
+ \@ifpackageloaded{tex4ht}
+ {\noexpand\iftrue}
+ {\noexpand\iffalse}}
+\makeatother
+
+
+% Define a text=none option for nodes that ignores the given text, from
+% https://tex.stackexchange.com/questions/59354/no-text-none-in-tikz
+\makeatletter
+\newif\iftikz@node@phantom
+\tikzset{
+ phantom/.is if=tikz@node@phantom,
+ text/.code=%
+ \edef\tikz@temp{#1}%
+ \ifx\tikz@temp\tikz@nonetext
+ \tikz@node@phantomtrue
+ \else
+ \tikz@node@phantomfalse
+ \let\tikz@textcolor\tikz@temp
+ \fi
+}
+\usepackage{etoolbox}
+\patchcmd\tikz@fig@continue{\tikz@node@transformations}{%
+ \iftikz@node@phantom
+ \setbox\pgfnodeparttextbox\hbox{}
+ \fi\tikz@node@transformations}{}{}
+\makeatother
+
+% Find the angle of a given line (within TikZ)
+\newcommand{\tikzAngleOfLine}{\tikz@AngleOfLine}
+\def\tikz@AngleOfLine(#1)(#2)#3{%
+ \pgfmathanglebetweenpoints{%
+ \pgfpointanchor{#1}{center}}{%
+ \pgfpointanchor{#2}{center}}
+ \pgfmathsetmacro{#3}{\pgfmathresult}%
+}
+
+% Now we define the global styles
+% The global styles are defined nestedly. You have to give your tikzpicture
+% the global options [vertexStyle, edgeStyle, faceStyle] to activate them.
+%
+% You can disable labels by using the option nolabels, i.e.
+% vertexStyle=nolabels to deactivate vertex labels.
+%
+% If you want to have a specific style for your picture, you can also use
+% this specific meta-style instead of the general style. For example if you
+% want to use double edges in one single picture - no matter the style of
+% the rest of the document - you can use edgeDouble instead of edgeStyle.
+%
+% To set the default style, modify the vertexStyle/.default entry.
+
+% Vertex styles
+\tikzset{
+ vertexNodePlain/.style = {fill=#1, shape=circle, inner sep=0pt, minimum size=2pt, text=none},
+ vertexNodePlain/.default=gray,
+ vertexPlain/labels/.style = {
+ vertexNode/.style={vertexNodePlain=##1},
+ vertexLabel/.style={gray}
+ },
+ vertexPlain/nolabels/.style = {
+ vertexNode/.style={vertexNodePlain=##1},
+ vertexLabel/.style={text=none}
+ },
+ vertexPlain/.style = vertexPlain/#1,
+ vertexPlain/.default=labels
+}
+\tikzset{
+ vertexNodeNormal/.style = {fill=#1, shape=circle, inner sep=0pt, minimum size=4pt, text=none},
+ vertexNodeNormal/.default = blue,
+ vertexNormal/labels/.style = {
+ vertexNode/.style={vertexNodeNormal=##1},
+ vertexLabel/.style={blue}
+ },
+ vertexNormal/nolabels/.style = {
+ vertexNode/.style={vertexNodeNormal=##1},
+ vertexLabel/.style={text=none}
+ },
+ vertexNormal/.style = vertexNormal/#1,
+ vertexNormal/.default=labels
+}
+\tikzset{
+ vertexNodeBallShading/pdf/.style = {ball color=#1},
+ vertexNodeBallShading/svg/.style = {fill=#1},
+ vertexNodeBallShading/.code = {% Conditional shading depending whether we want pdf or svg output
+ \if\texforht
+ \tikzset{vertexNodeBallShading/svg=#1!90!black}
+ \else
+ \tikzset{vertexNodeBallShading/pdf=#1}
+ \fi
+ },
+ vertexNodeBall/.style = {shape=circle, vertexNodeBallShading=#1, inner sep=2pt, outer sep=0pt, minimum size=3pt, font=\tiny},
+ vertexNodeBall/.default = white,
+ vertexBall/labels/.style = {
+ vertexNode/.style={vertexNodeBall=##1, text=black},
+ vertexLabel/.style={text=none}
+ },
+ vertexBall/nolabels/.style = {
+ vertexNode/.style={vertexNodeBall=##1, text=none},
+ vertexLabel/.style={text=none}
+ },
+ vertexBall/.style = vertexBall/#1,
+ vertexBall/.default=labels
+}
+\tikzset{
+ vertexStyle/.style={vertexNormal=#1},
+ vertexStyle/.default = labels
+}
+
+
+% 1) optional: colour of vertex
+% 2) position of the vertex
+% 3) relative position of the node
+% 4) name of the vertex
+\newcommand{\vertexLabelR}[4][]{
+ \ifthenelse{ \equal{#1}{} }
+ { \node[vertexNode] (#2 name) at (#2) {#4}; }
+ { \node[vertexNode=#1] (#2 name) at (#2) {#4}; }
+ \node[vertexLabel, #3] at (#2) {#4};
+}
+% 1) optional: colour of vertex
+% 2) position of the vertex
+% 3) absolute position of the node
+% 4) name of the vertex
+\newcommand{\vertexLabelA}[4][]{
+ \ifthenelse{ \equal{#1}{} }
+ { \node[vertexNode] (#2 name) at (#2) {#4}; }
+ { \node[vertexNode=#1] (#2 name) at (#2) {#4}; }
+ \node[vertexLabel] at (#3) {#4};
+}
+
+
+% Edge styles
+% If you have trouble with the double-lines overlapping, this might (?) help:
+% https://tex.stackexchange.com/questions/288159/closing-the-ends-of-double-line-in-tikz
+\newcommand{\edgeLabelColor}{blue!20!white}
+\tikzset{
+ edgeLineNone/.style = {draw=none},
+ edgeLineNone/.default=black,
+ edgeNone/labels/.style = {
+ edge/.style = {edgeLineNone=##1},
+ edgeLabel/.style = {fill=\edgeLabelColor,font=\small}
+ },
+ edgeNone/nolabels/.style = {
+ edge/.style = {edgeLineNone=##1},
+ edgeLabel/.style = {text=none}
+ },
+ edgeNone/.style = edgeNone/#1,
+ edgeNone/.default = labels
+}
+\tikzset{
+ edgeLinePlain/.style={line join=round, draw=#1},
+ edgeLinePlain/.default=black,
+ edgePlain/labels/.style = {
+ edge/.style={edgeLinePlain=##1},
+ edgeLabel/.style={fill=\edgeLabelColor,font=\small}
+ },
+ edgePlain/nolabels/.style = {
+ edge/.style={edgeLinePlain=##1},
+ edgeLabel/.style={text=none}
+ },
+ edgePlain/.style = edgePlain/#1,
+ edgePlain/.default = labels
+}
+\tikzset{
+ edgeLineDouble/.style = {very thin, double=#1, double distance=.8pt, line join=round},
+ edgeLineDouble/.default=gray!90!white,
+ edgeDouble/labels/.style = {
+ edge/.style = {edgeLineDouble=##1},
+ edgeLabel/.style = {fill=\edgeLabelColor,font=\small}
+ },
+ edgeDouble/nolabels/.style = {
+ edge/.style = {edgeLineDouble=##1},
+ edgeLabel/.style = {text=none}
+ },
+ edgeDouble/.style = edgeDouble/#1,
+ edgeDouble/.default = labels
+}
+\tikzset{
+ edgeStyle/.style = {edgePlain=#1},
+ edgeStyle/.default = labels
+}
+
+% Face styles
+% Here we have an exception - the style face is always defined.
+%
+\newcommand{\faceColorY}{yellow!60!white} % yellow
+\newcommand{\faceColorB}{blue!60!white} % blue
+\newcommand{\faceColorC}{cyan!60} % cyan
+\newcommand{\faceColorR}{red!60!white} % red
+\newcommand{\faceColorG}{green!60!white} % green
+\newcommand{\faceColorO}{orange!50!yellow!70!white} % orange
+
+% define default face colour (and default swap colour)
+\newcommand{\faceColor}{\faceColorY}
+\newcommand{\faceColorSwap}{\faceColorC}
+
+% define secondary default colours (to use in a single section)
+\newcommand{\faceColorFirst}{green!40!white}
+\newcommand{\faceColorSecond}{gray!15!white}
+\newcommand{\faceColorThird}{red!17!white}
+\newcommand{\faceColorFourth}{olive!20!white}
+
+\tikzset{
+ face/.style = {fill=#1},
+ face/.default = \faceColor,
+ faceY/.style = {face=\faceColorY},
+ faceB/.style = {face=\faceColorB},
+ faceC/.style = {face=\faceColorC},
+ faceR/.style = {face=\faceColorR},
+ faceG/.style = {face=\faceColorG},
+ faceO/.style = {face=\faceColorO}
+}
+\tikzset{
+ faceStyle/labels/.style = {
+ faceLabel/.style = {}
+ },
+ faceStyle/nolabels/.style = {
+ faceLabel/.style = {text=none}
+ },
+ faceStyle/.style = faceStyle/#1,
+ faceStyle/.default = labels
+}
+\tikzset{ face/.style={fill=#1} }
+\tikzset{ faceSwap/.code=
+ \ifdefined\swapColors
+ \tikzset{face=\faceColorSwap}
+ \else
+ \tikzset{face=\faceColor}
+ \fi
+}
+
+
+
+\usepackage{hyperref}
+
+
+\begin{document}
+
+
+
+\begin{tikzpicture}[vertexStyle, edgeStyle, faceStyle]
+
+ \newcommand{\setupCoord}{
+ \coordinate (Z) at (0,0);
+ \foreach \i in {1,...,6}{
+ \coordinate (A\i) at (180-60*\i:2.5);
+ }
+ }
+
+
+ \begin{scope}[shift={(3.5,0)}]
+ \setupCoord
+
+ \draw[edge,face]
+ (Z) -- (A1) -- node[edgeLabel]{1} (A2) -- cycle
+ (Z) -- node[edgeLabel]{9} (A2) -- node[edgeLabel]{2} (A3) -- cycle
+ (Z) -- node[edgeLabel]{10} (A3) -- node[edgeLabel]{3} (A4) -- cycle
+ (Z) -- node[edgeLabel]{11} (A4) -- node[edgeLabel]{4} (A5) -- cycle
+ (Z) -- node[edgeLabel]{12} (A5) -- node[edgeLabel]{5} (A6) -- cycle
+ (Z) -- node[edgeLabel]{13} (A6) -- node[edgeLabel]{6} (A1) -- node[edgeLabel]{8} (Z);
+
+ \foreach \p/\r/\n in {1/above left/1, 2/above right/2, 3/right/3, 4/below right/4, 5/below left/5, 6/left/6}{
+ \vertexLabelR{A\p}{\r}{\n}
+ }
+ \vertexLabelR{Z}{above}{8}
+
+ \foreach \i/\j/\n in {1/2/I, 2/3/II, 3/4/III, 4/5/IV, 5/6/V, 6/1/VI}{
+ \node[faceLabel] at (barycentric cs:A\i=1,A\j=1,Z=1) {$\n$};
+ }
+ \end{scope}
+
+
+ \node at (0,0) {$\rightarrow$};
+
+
+ \begin{scope}[shift={(-3.5,0)}]
+ \setupCoord
+
+ \draw[edge,face]
+ (Z) -- node[edgeLabel]{5} (A1) -- node[edgeLabel]{1} (A2) -- cycle
+ (Z) -- node[edgeLabel]{6} (A2) -- node[edgeLabel]{2} (A3) -- cycle
+ (Z) -- node[edgeLabel]{7} (A3) -- node[edgeLabel]{3} (A4) -- node[edgeLabel]{4} (Z);
+
+ \foreach \p/\r/\n in {1/above left/1, 2/above right/2, 3/right/3, 4/below right/4}{
+ \vertexLabelR{A\p}{\r}{\n}
+ }
+ \vertexLabelR{Z}{below left}{5}
+
+ \foreach \i/\j/\n in {1/2/I, 2/3/II, 3/4/III}{
+ \node[faceLabel] at (barycentric cs:A\i=1,A\j=1,Z=1) {$\n$};
+ }
+ \end{scope}
+\end{tikzpicture}
+
+\end{document}
diff --git a/gap/Morphisms/morphisms.gd b/gap/Morphisms/morphisms.gd
index c4f8da79..0294f658 100644
--- a/gap/Morphisms/morphisms.gd
+++ b/gap/Morphisms/morphisms.gd
@@ -422,6 +422,93 @@ DeclareOperation( "PolygonalIdentityMorphism", [IsPolygonalComplex] );
DeclareAttribute( "InversePolygonalMorphism", IsPolygonalMorphism and IsBijective );
+#!
+#!
+#! A polygonal morphism or fail
+#!
+#! Given two simplicial surfaces surf1 and surf2, return a butterfly-faithful
+#! monomorphism from surf1 to surf2 if it exists. Otherwise return fail.
+#! A homomorphism of simplicial surfaces is called butterfly-faithful if the homomorphism
+#! when restricted to a butterfly, i.e. restricted to two incident faces of an inner edge,
+#! becomes a bijection onto another butterfly. In other words, every butterfly of
+#! surf1 is being preserved and does not degenerate in surf2.
+#! As an example, consider the 3-half-umbrella and 6-umbrella.
+#!
+#! <br><img src="./images/_Wrapper_Butterfly_Faithful_Monomorphism_Hexagon.svg"> </img> <br>
+#!
+#!
+#! \begin{center}
+#! \includegraphics{images/_Wrapper_Butterfly_Faithful_Monomorphism_Hexagon.pdf}
+#! \end{center}
+#!
+#!
+#! Image omitted in terminal text
+#!
+#! @ExampleSession
+#! gap> six := SimplicialSurfaceByDownwardIncidence(
+#! > [[1,2],[2,3],[3,4],[4,5],[5,6],[6,1],,[1,8],[2,8],[3,8],[4,8],[5,8],[6,8]],
+#! > [[1,8,9],[2,9,10],[3,10,11],[4,11,12],[5,12,13],[6,13,8]]);;
+#! gap> three := SimplicialSurfaceByDownwardIncidence(
+#! > [[1,2],[2,3],[3,4],[5,4],[1,5],[2,5],[3,5]], [[1,5,6],[2,6,7],[3,7,4]]);;
+#! gap> mor_3_to_6 := ButterflyFaithfulMonomorphismIntoSimplicialSurface(three,
+#! > six);;
+#! gap> VertexMapAsImageList(mor_3_to_6);
+#! [ 1, 2, 3, 4, 8 ]
+#! gap> EdgeMapAsImageList(mor_3_to_6);
+#! [ 1, 2, 3, 11, 8, 9, 10 ]
+#! gap> FaceMapAsImageList(mor_3_to_6);
+#! [ 1, 2, 3 ]
+#! @EndExampleSession
+#!
+#!
+#
+DeclareOperation( "ButterflyFaithfulMonomorphismIntoSimplicialSurface", [IsSimplicialSurface, IsSimplicialSurface]);
+
+#!
+#!
+#! A list of polygonal morphisms
+#!
+#! Given two simplicial surfaces surf1 and surf2, return a list of all
+#! butterfly-faithful monomorphisms from surf1 to surf2.
+#! A homomorphism of simplicial surfaces is called butterfly-faithful if the homomorphism
+#! when restricted to a butterfly, i.e. restricted to two incident faces of an inner edge,
+#! becomes a bijection onto another butterfly. In other words, every butterfly of
+#! surf1 is being preserved and does not degenerate in surf2.
+#! As an example, consider the 3-half-umbrella and 6-umbrella. We would expect 12
+#! butterfly-faithful monomorphisms, namely by aligning the 3-half umbrella with 3
+#! consecutive faces of the 6-umbrella. There are 6 ways to do this and another 6 if
+#! we flip the 3-half-umbrella first.
+#!
+#! <br><img src="./images/_Wrapper_Butterfly_Faithful_Monomorphism_Hexagon.svg"> </img> <br>
+#!
+#!
+#! \begin{center}
+#! \includegraphics{images/_Wrapper_Butterfly_Faithful_Monomorphism_Hexagon.pdf}
+#! \end{center}
+#!
+#!
+#! Image omitted in terminal text
+#!
+#! @ExampleSession
+#! gap> six := SimplicialSurfaceByDownwardIncidence(
+#! > [[1,2],[2,3],[3,4],[4,5],[5,6],[6,1],,[1,8],[2,8],[3,8],[4,8],[5,8],[6,8]],
+#! > [[1,8,9],[2,9,10],[3,10,11],[4,11,12],[5,12,13],[6,13,8]]);;
+#! gap> three := SimplicialSurfaceByDownwardIncidence(
+#! > [[1,2],[2,3],[3,4],[5,4],[1,5],[2,5],[3,5]], [[1,5,6],[2,6,7],[3,7,4]]);;
+#! gap> all_mor_3_to_6 := AllButterflyFaithfulMonomorphismsIntoSimplicialSurface(
+#! > three, six);;
+#! gap> Length(all_mor_3_to_6);
+#! 12
+#! @EndExampleSession
+#!
+#!
+#
+DeclareOperation( "AllButterflyFaithfulMonomorphismsIntoSimplicialSurface", [IsSimplicialSurface, IsSimplicialSurface]);
+
#! @Section Images and pre-images
#! @SectionLabel Morphisms_Images
#!
@@ -1285,5 +1372,3 @@ DeclareAttribute( "VEFLabelMapAsImageList", IsPolygonalMorphism );
# attribute VertexMapping
# attribute EdgeMapping
# attribute FaceMapping
-
-
diff --git a/gap/Morphisms/morphisms.gi b/gap/Morphisms/morphisms.gi
index 521168f0..2d88d3a5 100644
--- a/gap/Morphisms/morphisms.gi
+++ b/gap/Morphisms/morphisms.gi
@@ -478,8 +478,334 @@ InstallMethod( InverseGeneralMapping, "for a polygonal morphism",
return InversePolygonalMorphism(polMor);
end
+);
+
+BindGlobal("__SIMPLICIAL_PermuteList",
+ function(list, perm)
+ return List(list, x -> x^perm);
+ end
+);
+
+BindGlobal("__SIMPLICIAL_AllBijections",
+ function(list1, list2)
+ local res, AllPermutations, i, bijection, g, tmp;
+ res := [];
+ if not (Length(list1) = Length(list2)) then
+ return [];
+ else
+ AllPermutations := [];
+ for g in SymmetricGroup(list2) do
+ Add(AllPermutations, __SIMPLICIAL_PermuteList(list2, g));
+ od;
+ for bijection in AllPermutations do
+ tmp := [];
+ for i in [1..Length(list1)] do
+ tmp[list1[i]] := bijection[i];
+ od;
+ Add(res, tmp);
+ od;
+ return res;
+ fi;
+ end
+);
+
+BindGlobal("__SIMPLICIAL_ImageCatFromListmap",
+ function(map, x_list)
+ local res, i;
+ res := [];
+ for i in x_list do
+ if IsBound(map[i]) then
+ Add(res, map[i]);
+ fi;
+ od;
+ return res;
+ end
);
+BindGlobal("__SIMPLICIAL_PreImageFromListmap",
+ function(map, y)
+ local res, i;
+ res := [];
+ for i in [1..Length(map)] do
+ if IsBound(map[i]) and map[i] = y then
+ Add(res, i);
+ fi;
+ od;
+ return res;
+ end
+);
+
+BindGlobal("__SIMPLICIAL_PreImagesCatFromListmap",
+ function(map, y_list)
+ return Concatenation(List(y_list, y -> __SIMPLICIAL_PreImageFromListmap(map, y)));
+ end
+);
+
+BindGlobal("__SIMPLICIAL_PreImagesFromListmap",
+ function(map)
+ return __SIMPLICIAL_PreImagesCatFromListmap(map, Compacted(map));
+ end
+);
+
+BindGlobal("__SIMPLICIAL_AllOneFaceIsomorphisms",
+ function(face1, surface1, face2, surface2)
+ local res, e, vertex_maps, vertex_map, mapped_e, i, edge, edge_map, face_map;
+ res := [];
+ vertex_maps := __SIMPLICIAL_AllBijections(VerticesOfFace(surface1, face1),
+ VerticesOfFace(surface2, face2));
+ for vertex_map in vertex_maps do
+ edge_map := [];
+ e := List([1,2,3], i -> Set(VerticesOfEdge(surface1, EdgesOfFace(surface1, face1)[i])));
+ mapped_e := List(e, e_i -> Set(List(e_i, v -> vertex_map[v])));
+ for i in [1..3] do
+ for edge in EdgesOfFace(surface2, face2) do
+ if Set(VerticesOfEdge(surface2, edge)) = Set(mapped_e[i]) then
+ edge_map[EdgesOfFace(surface1, face1)[i]]:= edge;
+ break;
+ fi;
+ od;
+ od;
+ face_map := [];
+ face_map[face1] := face2;
+ Add(res, [vertex_map, edge_map, face_map]);
+ od;
+ return res;
+ end
+);
+
+BindGlobal("__SIMPLICIAL_UpdateListmap",
+ function(map, element, image_of_element) # is used to update maps with new images, also updates when used in if-clauses !
+ if not IsBound(map[element]) then
+ map[element] := image_of_element;
+ return true;
+ elif map[element] = image_of_element then
+ return true;
+ else
+ return false;
+ fi;
+ end
+);
+
+BindGlobal("__SIMPLICIAL_IsInjectiveListmap",
+ function(map)
+ return IsDuplicateFree(Compacted(map));
+ end
+);
+
+InstallMethod(ButterflyFaithfulMonomorphismIntoSimplicialSurface, "for two simplicial surfaces",
+ [IsSimplicialSurface, IsSimplicialSurface],
+ function(surface1, surface2)
+ local possible_vertex_maps, possible_monomorphisms, starting_face, image_starting_face, mapped_edges, mapped_faces,
+ remaining_faces, cur_edges, cur_edge, mapped_vertices, monomorphism, cur_face, face_map, edge_map,
+ vertex_map, i, new_face, new_image_face, is_correct_morphism, new_vertex, new_image_vertex, new_edges, new_image_edges,
+ cur_new_edge, cur_new_image_edge, finished_cur_edges, faces1, faces2, face_counter1, face_counter2, face_degrees1, face_degrees2,
+ min_degrees, min_pos, possible_image_faces;
+
+ faces1 := Filtered(Faces(surface1), face -> ForAll(VerticesOfFace(surface1, face), v -> IsInnerVertex(surface1, v)));
+ faces2 := Filtered(Faces(surface2), face -> ForAll(VerticesOfFace(surface2, face), v -> IsInnerVertex(surface2, v)));
+ possible_image_faces := Faces(surface2);
+ if not faces1 = [] then # inner vertex degrees heuristic
+ face_counter1 := List(faces1, face -> Set(List(VerticesOfFace(surface1, face), v -> DegreeOfVertex(surface1, v))));
+ face_counter2 := List(faces1, face -> Set(List(VerticesOfFace(surface2, face), v -> DegreeOfVertex(surface2, v))));
+ if ForAny(face_counter2, fdegs2 -> not fdegs2 in face_counter1) then
+ return fail;
+ else
+ min_pos := PositionMinimum(List(Collected(face_counter2), fdeg2 -> fdeg2[2]));
+ min_degrees := Collected(face_counter2)[min_pos][1];
+ starting_face := Filtered(faces1, face -> min_degrees = Set(List(VerticesOfFace(surface1, face), v -> DegreeOfVertex(surface1, v))))[1];
+ possible_image_faces := Filtered(faces2, face -> min_degrees = Set(List(VerticesOfFace(surface2, face), v -> DegreeOfVertex(surface2, v))));
+ fi;
+ else
+ starting_face := Faces(surface1)[1];
+ fi;
+
+ for image_starting_face in possible_image_faces do
+ possible_monomorphisms := __SIMPLICIAL_AllOneFaceIsomorphisms(starting_face, surface1,
+ image_starting_face, surface2);
+ for i in [1..Length(possible_monomorphisms)] do
+ is_correct_morphism := true;
+ monomorphism := possible_monomorphisms[i];
+ vertex_map := monomorphism[1];
+ edge_map := monomorphism[2];
+ face_map := monomorphism[3];
+
+ cur_edges := Filtered(EdgesOfFace(surface1, starting_face), e -> IsInnerEdge(surface1, e));
+ finished_cur_edges := [];
+ remaining_faces := Difference(Faces(surface1), [starting_face]);
+ mapped_faces := __SIMPLICIAL_PreImagesFromListmap(face_map);
+ while not IsEmpty(remaining_faces) do
+ cur_edge := cur_edges[1];
+ # update face_map
+ if Length(Difference(FacesOfEdge(surface2, edge_map[cur_edge]), face_map)) = 1 then
+ new_image_face := Difference(FacesOfEdge(surface2, edge_map[cur_edge]), face_map)[1];
+ else
+ is_correct_morphism := false;
+ break;
+ fi;
+ new_face := Difference(FacesOfEdge(surface1, cur_edge), mapped_faces)[1];
+
+ if not new_face in remaining_faces then
+ is_correct_morphism := false;
+ break;
+ fi;
+
+ if __SIMPLICIAL_UpdateListmap(face_map, new_face, new_image_face) = false or __SIMPLICIAL_IsInjectiveListmap(face_map) = false then
+ is_correct_morphism := false;
+ break;
+ fi;
+ mapped_faces := __SIMPLICIAL_PreImagesFromListmap(face_map);
+
+ # update vertex_map
+ new_vertex := Difference(VerticesOfFace(surface1, new_face), VerticesOfEdge(surface1, cur_edge))[1];
+ new_image_vertex := Difference(VerticesOfFace(surface2, new_image_face), VerticesOfEdge(surface2, edge_map[cur_edge]))[1];
+ if __SIMPLICIAL_UpdateListmap(vertex_map, new_vertex, new_image_vertex) = false or __SIMPLICIAL_IsInjectiveListmap(vertex_map) = false then
+ is_correct_morphism := false;
+ break;
+ fi;
+
+ # update edge_map
+ new_edges := Difference(EdgesOfFace(surface1, new_face), [cur_edge]);
+ new_image_edges := Difference(EdgesOfFace(surface2, new_image_face), [edge_map[cur_edge]]);
+ for cur_new_edge in new_edges do
+ for cur_new_image_edge in new_image_edges do
+ if Set(__SIMPLICIAL_ImageCatFromListmap(vertex_map, VerticesOfEdge(surface1, cur_new_edge))) = Set(VerticesOfEdge(surface2, cur_new_image_edge)) then
+ if __SIMPLICIAL_UpdateListmap(edge_map, cur_new_edge, cur_new_image_edge) = false or __SIMPLICIAL_IsInjectiveListmap(edge_map) = false then
+ is_correct_morphism := false;
+ fi;
+ if cur_new_edge in cur_edges then
+ Add(finished_cur_edges, cur_new_edge);
+ fi;
+ fi;
+ od;
+ od;
+ if is_correct_morphism = false then
+ break;
+ fi;
+
+ # update cur_edges
+ cur_edges := Union(cur_edges, Filtered(EdgesOfFace(surface1, new_face), e -> IsInnerEdge(surface1, e)));
+ Add(finished_cur_edges, cur_edge);
+ cur_edges := Difference(cur_edges, finished_cur_edges);
+
+ # update remaining_faces
+ remaining_faces := Difference(remaining_faces, [new_face]);
+ od;
+
+ if is_correct_morphism = true then
+ return PolygonalMorphismByLists(surface1, surface2, monomorphism[1], monomorphism[2], monomorphism[3]);
+ fi;
+ od;
+ od;
+ return fail;
+ end
+);
+
+InstallMethod(AllButterflyFaithfulMonomorphismsIntoSimplicialSurface, "for two simplicial surfaces",
+ [IsSimplicialSurface, IsSimplicialSurface],
+ function(surface1, surface2)
+ local possible_vertex_maps, possible_monomorphisms, starting_face, image_starting_face, mapped_edges, mapped_faces,
+ remaining_faces, cur_edges, cur_edge, mapped_vertices, monomorphism, cur_face, face_map, edge_map,
+ vertex_map, i, new_face, new_image_face, is_correct_morphism, new_vertex, new_image_vertex, new_edges, new_image_edges,
+ cur_new_edge, cur_new_image_edge, finished_cur_edges, faces1, faces2, face_counter1, face_counter2, face_degrees1, face_degrees2,
+ min_degrees, min_pos, possible_image_faces, res;
+
+ res := [];
+ faces1 := Filtered(Faces(surface1), face -> ForAll(VerticesOfFace(surface1, face), v -> IsInnerVertex(surface1, v)));
+ faces2 := Filtered(Faces(surface2), face -> ForAll(VerticesOfFace(surface2, face), v -> IsInnerVertex(surface2, v)));
+ possible_image_faces := Faces(surface2);
+ if not faces1 = [] then # inner vertex degrees heuristic
+ face_counter1 := List(faces1, face -> Set(List(VerticesOfFace(surface1, face), v -> DegreeOfVertex(surface1, v))));
+ face_counter2 := List(faces1, face -> Set(List(VerticesOfFace(surface2, face), v -> DegreeOfVertex(surface2, v))));
+ if ForAny(face_counter2, fdegs2 -> not fdegs2 in face_counter1) then
+ return res; # empty list
+ else
+ min_pos := PositionMinimum(List(Collected(face_counter2), fdeg2 -> fdeg2[2]));
+ min_degrees := Collected(face_counter2)[min_pos][1];
+ starting_face := Filtered(faces1, face -> min_degrees = Set(List(VerticesOfFace(surface1, face), v -> DegreeOfVertex(surface1, v))))[1];
+ possible_image_faces := Filtered(faces2, face -> min_degrees = Set(List(VerticesOfFace(surface2, face), v -> DegreeOfVertex(surface2, v))));
+ fi;
+ else
+ starting_face := Faces(surface1)[1];
+ fi;
+
+ for image_starting_face in possible_image_faces do
+ possible_monomorphisms := __SIMPLICIAL_AllOneFaceIsomorphisms(starting_face, surface1,
+ image_starting_face, surface2);
+ for i in [1..Length(possible_monomorphisms)] do
+ is_correct_morphism := true;
+ monomorphism := possible_monomorphisms[i];
+ vertex_map := monomorphism[1];
+ edge_map := monomorphism[2];
+ face_map := monomorphism[3];
+
+ cur_edges := Filtered(EdgesOfFace(surface1, starting_face), e -> IsInnerEdge(surface1, e));
+ finished_cur_edges := [];
+ remaining_faces := Difference(Faces(surface1), [starting_face]);
+ mapped_faces := __SIMPLICIAL_PreImagesFromListmap(face_map);
+ while not IsEmpty(remaining_faces) do
+ cur_edge := cur_edges[1];
+ if Length(Difference(FacesOfEdge(surface2, edge_map[cur_edge]), face_map)) = 1 then
+ new_image_face := Difference(FacesOfEdge(surface2, edge_map[cur_edge]), face_map)[1];
+ else
+ is_correct_morphism := false;
+ break;
+ fi;
+ new_face := Difference(FacesOfEdge(surface1, cur_edge), mapped_faces)[1];
+
+ if not new_face in remaining_faces then
+ is_correct_morphism := false;
+ break;
+ fi;
+
+ if __SIMPLICIAL_UpdateListmap(face_map, new_face, new_image_face) = false or __SIMPLICIAL_IsInjectiveListmap(face_map) = false then
+ is_correct_morphism := false;
+ break;
+ fi;
+ mapped_faces := __SIMPLICIAL_PreImagesFromListmap(face_map);
+
+ new_vertex := Difference(VerticesOfFace(surface1, new_face), VerticesOfEdge(surface1, cur_edge))[1];
+ new_image_vertex := Difference(VerticesOfFace(surface2, new_image_face), VerticesOfEdge(surface2, edge_map[cur_edge]))[1];
+ if __SIMPLICIAL_UpdateListmap(vertex_map, new_vertex, new_image_vertex) = false or __SIMPLICIAL_IsInjectiveListmap(vertex_map) = false then
+ is_correct_morphism := false;
+ break;
+ fi;
+
+ # update edge_map
+ new_edges := Difference(EdgesOfFace(surface1, new_face), [cur_edge]);
+ new_image_edges := Difference(EdgesOfFace(surface2, new_image_face), [edge_map[cur_edge]]);
+ for cur_new_edge in new_edges do
+ for cur_new_image_edge in new_image_edges do
+ if Set(__SIMPLICIAL_ImageCatFromListmap(vertex_map, VerticesOfEdge(surface1, cur_new_edge))) = Set(VerticesOfEdge(surface2, cur_new_image_edge)) then
+ if __SIMPLICIAL_UpdateListmap(edge_map, cur_new_edge, cur_new_image_edge) = false or __SIMPLICIAL_IsInjectiveListmap(edge_map) = false then
+ is_correct_morphism := false;
+ fi;
+ if cur_new_edge in cur_edges then
+ Add(finished_cur_edges, cur_new_edge);
+ fi;
+ fi;
+ od;
+ od;
+ if is_correct_morphism = false then
+ break;
+ fi;
+
+ # update cur_edges
+ cur_edges := Union(cur_edges, Filtered(EdgesOfFace(surface1, new_face), e -> IsInnerEdge(surface1, e)));
+ Add(finished_cur_edges, cur_edge);
+ cur_edges := Difference(cur_edges, finished_cur_edges);
+
+ # update remaining_faces
+ remaining_faces := Difference(remaining_faces, [new_face]);
+ od;
+
+ if is_correct_morphism = true then
+ Add(res,PolygonalMorphismByLists(surface1, surface2, monomorphism[1], monomorphism[2], monomorphism[3]));
+ fi;
+ od;
+ od;
+ return res;
+ end
+);
##
## End of constructions
diff --git a/gap/Paths/paths.gi b/gap/Paths/paths.gi
index 2cc18fcf..155b552f 100644
--- a/gap/Paths/paths.gi
+++ b/gap/Paths/paths.gi
@@ -329,7 +329,7 @@ InstallMethod(ConcatenationOfPaths, "for a twisted polygonal complex and two ver
if Last(VerticesAsList(path1))<>VerticesAsList(path2)[1] then
Error("ConcatenationOfPaths: The last vertex of the first path has to be the first vertex of the second path");
fi;
- edges:=Union(EdgesAsList(path1), EdgesAsList(path2));
+ edges:=Concatenation(EdgesAsList(path1),EdgesAsList(path2));
return VertexEdgePathByEdges(surface, edges);
end
);
@@ -391,6 +391,7 @@ InstallMethod(ShiftCyclicPath,
end
);
+if IsPackageMarkedForLoading( "Digraphs", ">=1.9.0" ) then
InstallMethod(AllClosedVertexEdgePaths, "for a complex",
[IsTwistedPolygonalComplex],
function(complex)
@@ -406,20 +407,19 @@ InstallMethod(AllClosedVertexEdgePaths, "for a complex",
# First calculate all cycles of the edge graph.
# These cycles result in all closed vertex edge path of the complex except cycles of length two.
graph:=EdgeDigraphsGraph(newComplex);
- cycles:=__SIMPLICIAL_AllCyclesOfGraph(graph);
+ cycles:=DigraphAllUndirectedSimpleCircuits(graph);
newCycles:=[];
# Compute for all cycles the corresponding edges in the complex.
for c in cycles do
- nodesOfCycle:=Flat(__SIMPLICIAL_NodesOfCycle(c));
# CommonEdgesOfVertices returns all edges that are possible between two vertices.
edgeList:=[];
- for e in CommonEdgesOfVertices(newComplex, Last(nodesOfCycle),nodesOfCycle[1]) do
+ for e in CommonEdgesOfVertices(newComplex, Last(c),c[1]) do
Add(edgeList,[e]);
od;
- for i in [1..Length(nodesOfCycle)-1] do
- edges:=CommonEdgesOfVertices(newComplex, nodesOfCycle[i],nodesOfCycle[i+1]);
+ for i in [1..Length(c)-1] do
+ edges:=CommonEdgesOfVertices(newComplex, c[i],c[i+1]);
newEdgeList:=[];
# Add the possibilities for the next edge to each edge list computed so far.
@@ -444,6 +444,7 @@ InstallMethod(AllClosedVertexEdgePaths, "for a complex",
return Union(newCycles,AllTwoWaistsOfComplex(complex));
end
);
+fi;
#######################################
##
@@ -646,7 +647,7 @@ InstallMethod( EdgeFacePathByFaces,
local i;
__SIMPLICIAL_CheckEdge(complex, firstEdge, "EdgeFacePathByFaces");
- __SIMPLICIAL_CheckFace(complex, lastEdge, "EdgeFacePathByFaces");
+ __SIMPLICIAL_CheckEdge(complex, lastEdge, "EdgeFacePathByFaces");
if Length(faceList) > 0 then
__SIMPLICIAL_CheckFace(complex, faceList[1], "EdgeFacePathByFaces");
for i in [2..Length(faceList)] do
@@ -951,9 +952,9 @@ InstallMethod(ShiftCyclicPath,
InstallMethod(IsWaist, "for a complex and a vertex-edge path",
[IsTwistedPolygonalComplex, IsVertexEdgePath],
function(complex,path)
- local edges, incidentFaces, e, foe;
+ local edges, incidentFaces, e, foe, edgeGraph, vertices, subdigr, i, lastV, vertex;
+
edges:=EdgesAsList(path);
-
incidentFaces:=[];
if ForAll(edges, e->IsInnerEdge(complex,e)) and IsClosedPath(path) then
# check if the edges are not incident to the same faces
@@ -965,6 +966,21 @@ InstallMethod(IsWaist, "for a complex and a vertex-edge path",
return false;
fi;
od;
+
+ # check distance-faithful
+ if Length(edges)>3 then
+ edgeGraph:=EdgeDigraphsGraph(complex);
+ vertices:=ShallowCopy(VerticesAsList(path));
+ Remove(vertices);
+ subdigr:=InducedSubdigraph(edgeGraph,vertices);
+ lastV:=Position(DigraphVertexLabels(subdigr),Last(vertices));
+ for i in [1..Length(vertices)-1] do
+ vertex:=Position(DigraphVertexLabels(subdigr),vertices[i]);
+ if DigraphShortestDistance(edgeGraph,vertices[i],Last(vertices))<>DigraphShortestDistance(subdigr,vertex,lastV) then
+ return false;
+ fi;
+ od;
+ fi;
else
return false;
fi;
@@ -1018,22 +1034,28 @@ InstallMethod( AllThreeWaistsOfComplex, "for a twisted polygonal complex",
end
);
+if IsPackageMarkedForLoading( "Digraphs", ">=1.9.0" ) then
InstallMethod(AllWaistsOfComplex, "for a twisted polygonal complex",
[IsTwistedPolygonalComplex],
function(complex)
- local cycles, waists, c, edges, incident, incidentFaces, e, foe;
-
- cycles:=AllClosedVertexEdgePaths(complex);
+ local threeWaists, cycles, waists, c, edges, incident, incidentFaces, e, foe;
+
+ threeWaists:=AllThreeWaistsOfComplex(complex);
+ if threeWaists<>[] then
+ return Concatenation(threeWaists,AllTwoWaistsOfComplex(complex));
+ fi;
+ cycles:=AllClosedVertexEdgePaths(complex);
waists:=[];
for c in cycles do
- if IsWaist(complex,c) then
- Add(waists,c);
+ if IsWaist(complex,c) then
+ Add(waists,c);
fi;
od;
return waists;
end
);
+fi;
##
## End of waists
##
diff --git a/gap/PolygonalComplexes/animating.gd b/gap/PolygonalComplexes/animating.gd
index 67ce9b83..f01b0e32 100644
--- a/gap/PolygonalComplexes/animating.gd
+++ b/gap/PolygonalComplexes/animating.gd
@@ -143,12 +143,15 @@ DeclareOperation( "SetVertexCoordinates3DNC", [IsSimplicialSurface, IsList, IsRe
#! The 3D-coordinates of vertex vertex has to have the format [x,y,z].
#! If the format of the coordinates is not correct, then an error is shown.
#! This can happen, if the NC version is used to store the 3D-coordinates.
-#! The NC-version does not check the coordinate format saved in the print record.
+#! The NC-version does not check the coordinate format saved in the print record.
+#! The GetAllVertexCoordinates3DNC method returns all coordinates from the print record without checking them as a list.
#! @Returns a list
#! @Arguments surface, vertex, printRecord
DeclareOperation( "GetVertexCoordinates3D", [IsSimplicialSurface, IsPosInt, IsRecord] );
#! @Arguments surface, vertex, printRecord
DeclareOperation( "GetVertexCoordinates3DNC", [IsSimplicialSurface, IsPosInt, IsRecord] );
+#! @Arguments surface, printRecord
+DeclareOperation( "GetAllVertexCoordinates3DNC", [IsSimplicialSurface, IsRecord] );
#! @EndGroup
#! @BeginGroup DrawSurfaceToJavaScript
diff --git a/gap/PolygonalComplexes/animating.gi b/gap/PolygonalComplexes/animating.gi
index 9a01ddd2..d0e8c648 100644
--- a/gap/PolygonalComplexes/animating.gi
+++ b/gap/PolygonalComplexes/animating.gi
@@ -88,6 +88,14 @@ InstallMethod( GetVertexCoordinates3DNC,
RedispatchOnCondition(GetVertexCoordinates3DNC, true, [IsTwistedPolygonalComplex, IsPosInt, IsRecord],
[IsSimplicialSurface], 0);
+InstallMethod( GetAllVertexCoordinates3DNC,
+ "for a simplicial surface and a record",
+ [IsSimplicialSurface, IsRecord],
+ function(surface, printRecord)
+ return printRecord.vertexCoordinates3D;
+ end
+);
+
InstallMethod( CalculateParametersOfInnerCircle,
"for a simplicial surface and a record",
diff --git a/gap/PolygonalComplexes/drawing.gd b/gap/PolygonalComplexes/drawing.gd
index a973a60c..662dae41 100644
--- a/gap/PolygonalComplexes/drawing.gd
+++ b/gap/PolygonalComplexes/drawing.gd
@@ -1303,7 +1303,7 @@ DrawFacegraphToTikz( tetra, "facegraph_oct_rescaled", pr);;
#! work:
#!
#! @BeginLog
-tetra :=SimplicialSurfaceByVerticesInFaces([[1,2,3],[1,2,4],
+tetra := SimplicialSurfaceByVerticesInFaces([[1,2,3],[1,2,4],
[1,3,4],[2,3,4]]);;
DrawFacegraphToTikz(tetra, "facegraph_tetrahedron");;
#! @EndLog
@@ -1344,12 +1344,10 @@ DrawFacegraphToTikz(tetra,
#!
#! Image omitted in terminal text
#!
-#!
#!
#!
#! @EndChunk
-
#! @BeginChunk DrawFacegraphToTikz_Geodesics
#! This subsection covers the usage of the parameter that adds geodesics into
#! the drawings of DrawFacegraphToTikz
@@ -1429,5 +1427,4 @@ DrawFacegraphToTikz( oct,
#! is false) the generated tex-file will only consist of a
#! tikzpicture without header (it is not possible to compile it on
#! its own).
-#! @EndChunk
-
+#! @EndChunk
\ No newline at end of file
diff --git a/gap/PolygonalComplexes/drawing.gi b/gap/PolygonalComplexes/drawing.gi
index f6942b07..a7202043 100644
--- a/gap/PolygonalComplexes/drawing.gi
+++ b/gap/PolygonalComplexes/drawing.gi
@@ -2022,5 +2022,3 @@ InstallOtherMethod( DrawFacegraphToTikz,
return DrawFacegraphToTikz(surface,file,rec());
end
);
-
-
diff --git a/gap/PolygonalComplexes/embedding.gd b/gap/PolygonalComplexes/embedding.gd
index 95e41cdd..1fe18f8f 100644
--- a/gap/PolygonalComplexes/embedding.gd
+++ b/gap/PolygonalComplexes/embedding.gd
@@ -382,4 +382,4 @@ DeclareOperation( "DrawFacegraphToTikz", [IsSimplicialSurface ,IsString,IsRecord
#! @Subsection Output control
#! @SubsectionLabel DrawFacegraphToTikz_Output
-#! @InsertChunk DrawFacegraphToTikz_Output
+#! @InsertChunk DrawFacegraphToTikz_Output
\ No newline at end of file
diff --git a/gap/PolygonalComplexes/graphs.gd b/gap/PolygonalComplexes/graphs.gd
index 95be24f0..73d6d30c 100644
--- a/gap/PolygonalComplexes/graphs.gd
+++ b/gap/PolygonalComplexes/graphs.gd
@@ -441,17 +441,16 @@ DeclareAttribute( "FaceNautyGraph", IsPolygonalComplex );
#! Image omitted in terminal text
#!
#!
-#! @BeginExampleSession
+#! @BeginLogSession
#! gap> digraph:=CompleteDigraph(4);;
#! gap> tet1 := AllSimplicialSurfacesOfDigraph(digraph,true);
#! [ simplicial surface (4 vertices, 6 edges, and 4 faces) ]
#! gap> IsIsomorphic(tet1[1],Tetrahedron());
#! true
-#! @EndExampleSession
-#!
+#! @EndLogSession
#! So the only vertex-faithful simplicial surface of the digraph is the tetrahedron.
#! But there is another simplicial surface, which is not vertex-faithful:
-#! @BeginExampleSession
+#! @BeginLogSession
#! gap> list := AllSimplicialSurfacesOfDigraph(digraph,false);
#! [ simplicial surface (4 vertices, 6 edges, and 4 faces),
#! simplicial surface (3 vertices, 6 edges, and 4 faces)]
@@ -459,7 +458,7 @@ DeclareAttribute( "FaceNautyGraph", IsPolygonalComplex );
#! [ simplicial surface (4 vertices, 6 edges, and 4 faces) ]
#! gap> IsIsomorphic(tet2[1],Tetrahedron());
#! true
-#! @EndExampleSession
+#! @EndLogSession
#!
#! Since it takes a long time to compute all cycles, you should only call the method for digraphs with twelve or less nodes for vertexfaithful false.
#! For vertexfaithful true, the method needs to consider only chordless and non-separating cycles. This makes the method fast for digraphs up to 28 nodes.
diff --git a/gap/PolygonalComplexes/graphs.gi b/gap/PolygonalComplexes/graphs.gi
index a31d9e9e..c038db10 100644
--- a/gap/PolygonalComplexes/graphs.gi
+++ b/gap/PolygonalComplexes/graphs.gi
@@ -722,353 +722,47 @@ InstallMethod( OnEdgeFacePaths,
##
## All Surfaces Of A Graph
##
+if IsPackageMarkedForLoading( "Digraphs", ">=1.9.0" ) then
+BindGlobal("__SIMPLICIAL_EdgesFromCycle",
+ function(digraph,cycle)
-# Calculate from a adjacency matrix the corresponding edges
-BindGlobal( "__SIMPLICIAL_EdgesFromAdjacencyMat",
- function(mat)
- local edges, i, j;
-
- edges := Set([]);
- for j in [1 .. Length(mat)] do
- edges := Union(edges, List(ListBlist([1..j],mat[j]), i-> [j,i] ));
- od;
-
- return edges;
-end);
-
-# The function converts a boolean list describing one or more cycles
-# into lists of nodes of the cycles.
-BindGlobal("__SIMPLICIAL_NodesOfCycle",
- function(cycle)
- local edges,firstNod,actualNod,nodes,found,e,cycles;
-
- edges:=__SIMPLICIAL_EdgesFromAdjacencyMat(cycle);
- cycles:=[];
- # We have to use each edge exactly one time
- while edges<>[] do
- firstNod:=(edges[1])[1];
- actualNod:=(edges[1])[2];
- Remove(edges,1);
- nodes:=[actualNod];
- # Walk along the cycle
- while actualNod<>firstNod do
- found:=false;
- for e in edges do
- if found=false then
- if e[1]=actualNod then
- actualNod:=e[2];
- Add(nodes,actualNod);
- Remove(edges,Position(edges,e));
- found:=true;
- elif e[2]=actualNod then
- actualNod:=e[1];
- Add(nodes,actualNod);
- Remove(edges,Position(edges,e));
- found:=true;
- fi;
- fi;
- od;
- od;
- Add(cycles,nodes);
- od;
- return cycles;
-end);
-
-# We want to store graphs as adjacency matrices. This function
-# turns a cycle into a boolean lower triangular matrix.
-BindGlobal("__SIMPLICIAL_AdjacencyMatrixFromList",
- function(cycle, n)
-
- local mat, i, j, k;
-
- mat := [];
- for i in [ 1 .. n] do
- mat[i] := BlistList([1..i],[]);
- od;
-
- for i in [ 1 .. Length(cycle)] do
- if i < Length(cycle) then
- j := cycle[i+1];
- else
- j := cycle[1];
- fi;
- k := cycle[i];
- if k < j then
- mat[j]:= UnionBlist(mat[j],BlistList( [1..j],[k] ));
- elif j < k then
- mat[k] := UnionBlist(mat[k],BlistList([1..k],[j]));
- fi;
- od;
-
- return mat;
-end);
-
-
-# The following function computes all cycles of a graph by:
-# 1. Calculation of a cycle base (CyclesBasisOfGraph) by first computing a spanning tree and then adding each edge
-# which is not in the tree. Each edge gives a different cycle named base cycles.
-# 2. Iterate through all possible combinations of base cycles and combine them by applying XOR to the adjacency matrices.
-# In this way you get all cycles of the graph.
-BindGlobal("__SIMPLICIAL_AllCyclesOfGraph",
- function(digraph)
-
- local CycleOnEdge,CycleBasisOfGraph,XORAdjacencyMatrices,
- MultipleCyclesInMatrix,cyclebasis, allcycles, nullmat, mat, i, k, pos, neighs,c,cycle;
-
- # Find a cycle in the original graph all of whose edges are in
- # tree except for the edge e.
- CycleOnEdge := function( tree, root, e )
-
- local cycle, i, path1, path2, l1, l2;
-
- if e[1] = e[2] then
- Error("CycleOnEdge: edge is a loop");
- return false;
- fi;
-
- # first find paths from the root to the two vertices of e
- if root = e[1] then path1 := [root];
- else path1 := DigraphPath(tree, root, e[1])[1]; fi;
- if root = e[2] then path2 := [root];
- else path2 := DigraphPath(tree, root, e[2])[1]; fi;
- l1 := Length(path1); l2 := Length(path2);
-
- # now skip the common entries in the path
- cycle := []; i := 1;
- while i <= Minimum(l1,l2) and path1[i] = path2[i] do
- i := i + 1;
- od;
-
- cycle := [path1[i-1]];
- Append(cycle, path1{[i..l1]});
- Append(cycle, Reversed(path2{[i..l2]}));
-
- return __SIMPLICIAL_AdjacencyMatrixFromList(cycle,DigraphNrVertices(tree));
- end;;
-
- # This function computes a cycle basis for the undirected graph
- # dig, which is assumed to be a symmetric digraph.
- # The cycle Basis consists of lower triangular matrices whose
- # entries are boolean lists.
- CycleBasisOfGraph := function( dig )
-
- local tree, dige, base, root, e;
-
- if not IsSymmetricDigraph(dig) then
- Error("the digraph is assumed to be symmetric");
- fi;
-
- base := Set([]);
- tree := UndirectedSpanningTree(dig);
- dige := DigraphEdges(tree);
-
- root := DigraphVertices(dig)[1];
-
- for e in DigraphEdges(dig) do
- if not e in dige then
- AddSet(base, CycleOnEdge(tree,root, e));
- fi;
- od;
-
-
- return base;
- end;;
-
- # This method combines two adjacency matrices with the operator XOR.
- XORAdjacencyMatrices := function( mat1, mat2 )
-
- local j, res, nd;
+ local edgesOfCycle, i, edge;
- res :=[];
- for j in [1.. Length(mat1)] do
- nd := IntersectionBlist(mat1[j],mat2[j]);
- FlipBlist(nd);
- res[j] := IntersectionBlist( UnionBlist(mat1[j],mat2[j]), nd );
- od;
-
- return res;
- end;;
-
- cyclebasis := CycleBasisOfGraph( digraph );
-
- if cyclebasis=[] then
- return [];
- fi;
-
- neighs := OutNeighbours(digraph);
- allcycles := [];
-
- nullmat := XORAdjacencyMatrices(cyclebasis[1],cyclebasis[1]);
- for k in [0..2^Length(cyclebasis)-1] do
- # combine the matrices encoded by k
- mat := nullmat;
- i := k;
- pos := 0;
- while i>0 do
- pos := pos + 1;
- if i mod 2 <> 0 then
- mat := XORAdjacencyMatrices(mat,cyclebasis[pos]);
- fi;
- i := Int(i/2);
- od;
-
- if SizeBlist(Flat(mat))<>0 then
- for c in __SIMPLICIAL_NodesOfCycle(mat) do
- cycle:=__SIMPLICIAL_AdjacencyMatrixFromList(c,Length(mat));
- if not cycle in allcycles then
- Add(allcycles,cycle);
- fi;
- od;
+ edgesOfCycle:=[];
+ for i in [1..Length(cycle)] do
+ if i0 then
- blocked[u]:=blocked[u]-1;
- fi;
- od;
- return blocked;
- end;
-
- # Computes the degree labeling
- DegreeLabeling:=function(digraph)
- local degree,color,labeling,v,u,i,minDegree,x;
-
- degree:=List(DigraphVertices(digraph),i->0);
- color:=List(DigraphVertices(digraph),i->false);
- labeling:=List(DigraphVertices(digraph),i->0);
- degree:=List(DigraphVertices(digraph),i->OutDegreeOfVertex(digraph,i));
-
- for i in [1..DigraphNrVertices(digraph)] do
- minDegree:=DigraphNrVertices(digraph);
- for x in DigraphVertices(digraph) do
- if color[x]=false and degree[x]key and blocked[v]=1 then
- extendedPath:=Concatenation(path,[v]);
- if IsDigraphEdge(digraph,v,First(path)) then
- Add(C,extendedPath);
- else
- data:=CCExtension(digraph,extendedPath,C,key,blocked);
- C:=data[1];
- blocked:=data[2];
- fi;
- fi;
- od;
- blocked:=UnblockNeighbours(digraph,Last(path),blocked);
- return [C,blocked];
- end;
-
- SetDigraphVertexLabels(digraph,DegreeLabeling(digraph));
- temp:=Triplets(digraph);
- T:=temp[1];
- C:=temp[2];
- blocked:=List(DigraphVertices(digraph),i->0);
- while T<>[] do
- triple:=Remove(T);
- blocked:=BlockNeighbours(digraph,triple[2],blocked);
- temp:=CCExtension(digraph,triple,C,DigraphVertexLabel(digraph,triple[2]),blocked);
- C:=temp[1];
- blocked:=temp[2];
- blocked:=UnblockNeighbours(digraph,triple[2],blocked);
- od;
- return C;
+ return edgesOfCycle;
- end
+ end
);
BindGlobal("__SIMPLICIAL_IsNonSeparating",
function(digraph,cycle)
- local edgesOfCycle, e, digraphRemoved;
+ local edgesOfCycle, e, digraphRemoved;
- if not IsSymmetricDigraph(digraph) then
- return false;
- fi;
+ if not IsSymmetricDigraph(digraph) then
+ return false;
+ fi;
- edgesOfCycle:=[];
- for e in __SIMPLICIAL_EdgesFromAdjacencyMat(cycle) do
- Append(edgesOfCycle,[e,Reversed(e)]);
- od;
+ edgesOfCycle:=__SIMPLICIAL_EdgesFromCycle(digraph,cycle);
- digraphRemoved:=DigraphRemoveEdges(digraph,edgesOfCycle);
- if IsConnectedDigraph(digraphRemoved) then
- return true;
- else
- return false;
- fi;
+ digraphRemoved:=DigraphRemoveEdges(digraph,edgesOfCycle);
+ if IsConnectedDigraph(digraphRemoved) then
+ return true;
+ else
+ return false;
+ fi;
- end
+ end
);
InstallOtherMethod(AllSimplicialSurfacesOfDigraph,"for a digraph",
@@ -1083,23 +777,23 @@ InstallMethod(AllSimplicialSurfacesOfDigraph,"for a digraph and a Boolean",
function(digraph,vertexFaithful)
local allCycles,edgesOfGraph, faces,edgesOfCycles,CyclesOfEdges,cyclesOfEdges,FindSurface,FindCycleComb,
- NodesOfCycle,cycle,cyclePair,IsPartOf,possibleCyclesPairs,commonEdges,Possible,e;
+ cycle,cyclePair,IsPartOf,possibleCyclesPairs,commonEdges,Possible,e;
if IsMultiDigraph(digraph) or DigraphHasLoops(digraph) or not IsSymmetricDigraph(digraph) or not IsConnectedDigraph(digraph) then
Error("SimplicialSurfaceOfDigraph: Given digraph has to be simple, symmetric and connected");
fi;
if vertexFaithful then
- allCycles:=List(__SIMPLICIAL_AllChordlessCyclesOfGraph(digraph),c->__SIMPLICIAL_AdjacencyMatrixFromList(c,DigraphNrVertices(digraph)));
- allCycles:=Filtered(allCycles,c->__SIMPLICIAL_IsNonSeparating(digraph,c));
+ allCycles:=DigraphAllChordlessCycles(digraph);
+ allCycles:=Filtered(allCycles,c->__SIMPLICIAL_IsNonSeparating(digraph,c));
else
- allCycles:=__SIMPLICIAL_AllCyclesOfGraph(digraph);
+ allCycles:=DigraphAllUndirectedSimpleCircuits(digraph);
fi;
edgesOfGraph:=Filtered(DigraphEdges(digraph),e->not IsSortedList(e));
edgesOfCycles:=[];
for cycle in [1..Length(allCycles)] do;
- edgesOfCycles[cycle]:=List(__SIMPLICIAL_EdgesFromAdjacencyMat(allCycles[cycle]),e->Position(edgesOfGraph,e));
+ edgesOfCycles[cycle]:=List(__SIMPLICIAL_EdgesFromCycle(digraph,allCycles[cycle]),e->Position(edgesOfGraph,e));
od;
possibleCyclesPairs:=[];
@@ -1152,34 +846,6 @@ InstallMethod(AllSimplicialSurfacesOfDigraph,"for a digraph and a Boolean",
return true;
end;;
- # The function computes for a given cycle the sequence of nodes of the cycle.
- NodesOfCycle:=function(cycle)
- local edges,firstNod,actualNode,nodes,e;
-
- edges:=__SIMPLICIAL_EdgesFromAdjacencyMat(cycle);
-
- firstNod:=(edges[1])[1];
- actualNode:=(edges[1])[2];
- nodes:=[actualNode];
- Remove(edges,1);
-
- while actualNode<>firstNod do
- for e in edges do
- if e[1]=actualNode then
- actualNode:=e[2];
- Add(nodes,actualNode);
- Remove(edges,Position(edges,e));
- elif e[2]=actualNode then
- actualNode:=e[1];
- Add(nodes,actualNode);
- Remove(edges,Position(edges,e));
- fi;
- od;
- od;
-
- return CycleFromList(nodes);
- end;;
-
IsPartOf:=function(face,faces)
local f;
for f in faces do
@@ -1195,10 +861,10 @@ InstallMethod(AllSimplicialSurfacesOfDigraph,"for a digraph and a Boolean",
# if we search vertex-faithful simplicial surfaces all cycles of length three and four have to be part of the resulting cycle combination
if vertexFaithful and IsIsomorphicDigraph(graph, CompleteDigraph(4)) then
- smallCy:=Filtered([1..Length(allCycles)], c->SizeBlist(Flat(allCycles[c]))<4);
+ smallCy:=Filtered(allCycles, c->Length(c)<4);
smallCy:=BlistList([1..Length(allCycles)],smallCy);
elif vertexFaithful then
- smallCy:=Filtered([1..Length(allCycles)], c->SizeBlist(Flat(allCycles[c]))<5);
+ smallCy:=Filtered(allCycles, c->Length(c)<5);
smallCy:=BlistList([1..Length(allCycles)],smallCy);
fi;
@@ -1257,7 +923,7 @@ InstallMethod(AllSimplicialSurfacesOfDigraph,"for a digraph and a Boolean",
umbrellaDesk:=[];
for cycle in ListBlist([1..Length(vertexCycleComb)],vertexCycleComb) do
- Add(umbrellaDesk,NodesOfCycle(allCycles[cycle]));
+ Add(umbrellaDesk,CycleFromList(allCycles[cycle]));
od;
face:=SimplicialSurfaceByUmbrellaDescriptor(umbrellaDesk);
@@ -1377,3 +1043,4 @@ InstallMethod(AllSimplicialSurfacesOfDigraph,"for a digraph and a Boolean",
end
);
+fi;
\ No newline at end of file
diff --git a/gap/PolygonalComplexes/modification.gd b/gap/PolygonalComplexes/modification.gd
index 8e68605a..8c26ba03 100755
--- a/gap/PolygonalComplexes/modification.gd
+++ b/gap/PolygonalComplexes/modification.gd
@@ -576,6 +576,22 @@ DeclareOperation( "SplitEdgePathNC", [IsPolygonalComplex, IsVertexEdgePath and I
#! The NC-version does not check whether the given set of faces
#! actually consists only of faces in complex. It also does not
#! check whether the result of SubsurfaceByFaces is a surface.
+#!
+#! In Chapter the edge colouring of twisted
+#! polygonal complexes will be introduced.
+#!
+#! The hexagon from above can be coloured as follows:
+#! @BeginExampleSession
+#! gap> colEdges:=[ 1, 2, 1, 2, 1, 2, 3, 3, 3, 3, 3, 3 ];;
+#! gap> colSurface:=EdgeColouredPolygonalComplex(hex,colEdges);
+#! tame coloured surface (MMB with 7 vertices, 12 edges and 6 faces)
+#! @EndExampleSession
+#! If we compute a subcomplex of an edge-coloured complex, it will be edge-coloured again,
+#! induced by the edge-colouring of the given complex:
+#! @BeginExampleSession
+#! gap> SubsurfaceByFaces(colSurface,[1,2]);
+#! tame coloured surface (BMB with 4 vertices, 5 edges and 2 faces)
+#! @EndExampleSession
#!
#! @Returns a twisted polygonal complex
#! @Arguments complex, faces
diff --git a/gap/PolygonalComplexes/modification.gi b/gap/PolygonalComplexes/modification.gi
index ef540ba2..545862e7 100755
--- a/gap/PolygonalComplexes/modification.gi
+++ b/gap/PolygonalComplexes/modification.gi
@@ -309,7 +309,7 @@ BindGlobal( "__SIMPLICIAL_ComputeNewVertexEdgePaths",
Add(extOld, PathAsList(vePath)[i]);
Add(newPaths, [ extNew, extOld ]);
-
+ new := new + 1;
used:=true;
fi;
od;
@@ -498,7 +498,8 @@ InstallOtherMethod( SubcomplexByFaces,
InstallMethod( SubcomplexByFacesNC, "for a polygonal complex and a set of faces",
[IsPolygonalComplex, IsSet],
function(complex, subfaces)
- local subVertices, subEdges, newVerticesOfEdges, newEdgesOfFaces, e, f;
+ local subVertices, subEdges, newVerticesOfEdges, newEdgesOfFaces, e, f,
+ subcomplex, colEdges, colEdgesSub, edge;
subEdges := __SIMPLICIAL_UnionSets( EdgesOfFaces(complex){subfaces} );
@@ -514,8 +515,18 @@ InstallMethod( SubcomplexByFacesNC, "for a polygonal complex and a set of faces"
newEdgesOfFaces[f] := EdgesOfFaces(complex)[f];
od;
- return PolygonalComplexByDownwardIncidenceNC( subVertices, subEdges,
+ subcomplex:=PolygonalComplexByDownwardIncidenceNC( subVertices, subEdges,
subfaces, newVerticesOfEdges, newEdgesOfFaces );
+ if IsEdgeColouredPolygonalComplex(complex) then
+ colEdges:=ColoursOfEdges(complex);
+ colEdgesSub:=[];
+ for edge in Edges(subcomplex) do
+ colEdgesSub[edge]:=colEdges[edge];
+ od;
+ subcomplex:=EdgeColouredPolygonalComplexNC(subcomplex,colEdgesSub);
+ fi;
+
+ return subcomplex;
end
);
InstallMethod( SubcomplexByFacesNC,
@@ -523,7 +534,7 @@ InstallMethod( SubcomplexByFacesNC,
[IsTwistedPolygonalComplex, IsSet],
function(complex, subfaces)
local remChambers, vofC, eofC, fofC, c, zeroClass, oneClass,
- twoClass, cl;
+ twoClass, cl, subcomplex, colEdges, colEdgesSub, edge;
remChambers := Union( ChambersOfFaces(complex){subfaces} );
vofC := [];
@@ -541,7 +552,16 @@ InstallMethod( SubcomplexByFacesNC,
twoClass := Set(twoClass);
twoClass := Difference(twoClass, [[]]);
- return TwistedPolygonalComplexByChamberRelationsNC( vofC, eofC, fofC, zeroClass, oneClass, twoClass );
+ subcomplex:=TwistedPolygonalComplexByChamberRelationsNC( vofC, eofC, fofC, zeroClass, oneClass, twoClass );
+ if IsEdgeColouredTwistedPolygonalComplex(complex) then
+ colEdges:=ColoursOfEdges(complex);
+ colEdgesSub:=[];
+ for edge in Edges(subcomplex) do
+ colEdgesSub[edge]:=colEdges[edge];
+ od;
+ subcomplex:=EdgeColouredTwistedPolygonalComplexNC(subcomplex,colEdgesSub);
+ fi;
+ return subcomplex;
end
);
InstallOtherMethod( SubcomplexByFacesNC,
diff --git a/makedoc.g b/makedoc.g
index f28e054a..0ffcdf1f 100644
--- a/makedoc.g
+++ b/makedoc.g
@@ -8,491 +8,25 @@ if fail = LoadPackage("GAPDoc", "1.6") then
QUIT_GAP(1);
fi;
-
-# We generate the names automatically by a hash-function. Therefore we
-# have to clean up old files periodically. To know which files have
-# become obsolete, we save the current files in this global list.
-BindGlobal("__SIMPLICIAL_ImageNames",[]);
-MakeReadWriteGlobal("__SIMPLICIAL_ImageNames");
-
-# We specify the documentation directory directly since we want to use it
-# in our preprocessing method.
-BindGlobal("__SIMPLICIAL_DocDirectory", "doc/");
-
-
-# The additional LaTeX-header to make tikz-pictures
-# Careful: The file TikZHeader.tex has to lie in the same directory as
-# the primary documentation file and the generated image files.
-BindGlobal("__SIMPLICIAL_TikZHeader", "\\input{TikZHeader.tex}\n\n" );
-
# We load the package since we need it for some computations in the manual
if fail = LoadPackage("SimplicialSurfaces") then
Error("SimplicialSurfaces package has to be available.");
fi;
-Exec( "pwd > _TMP_foo");
-manualposition := StringFile("_TMP_foo");
-Exec("rm _TMP_foo");
-RemoveCharacters(manualposition, "\n");
-manualposition := Concatenation(manualposition,"/doc/manual.pdf");
-manualposition := Concatenation("You can find the image in the manual. Most probably it is here: ", manualposition);
-
-# Now we have the XML-tree of the documentation
-# We need to change the -Tags into proper GAPDoc tags
-# For that we define a function that changes one node
-preProcessTikz := function( node )
- local cont, n1, n2, n3, file, output, name, htmlString, consoleString,
- path, tmpName, tmpFile, sysDirPath, md5, out, inStream, outStream,
- hash, tmpImageName, latexString;
-
- if node.name = "Alt" and IsBound(node.attributes.Only) and
- node.attributes.Only in ["TikZ","Tikz"] then
-
- # get the content of the tag
- cont := GetTextXMLTree(node);
-
-
- # We want to save our image in the file
- # _IMAGE_.tex
- # (that gives _IMAGE_-1.svg)
- #
- # This gives us the benefit of recognizing duplicates and
- # minimizing compiling time (especially if we don't change the pictures
- # between different versions of the code).
- #
- # At the same time this is problematic if we have \input-clauses inside
- # our document since a change in the referred document should be
- # reflected in a different overall image.
- #
- # To circumvent this issue, we proceed as follows:
- # 1) Write the picture into a temporary file _IMAGE_TMP.tex
- # We use a sed-command to remove all leading whitespaces
- # (otherwise the same picture might be compiled twice).
- # 2) Call ../flatex/flatex _IMAGE_TMP.tex
- # This creates the file _IMAGE_TMP.flt
- # 3) Calculate the hash of this file
- # 4) Check if _IMAGE_-1.svg (and the tex-file) already exist
- # If they do, do nothing.
- # 5) Otherwise call mv _IMAGE_TMP.tex _IMAGE_.tex
- # 6) Compile this image via htlatex _IMAGE_.tex
- # 7) Store the name _IMAGE_ in our list
- # 8) Include _IMAGE_-1.svg in the HTML document
-
- # Step 1
- path := __SIMPLICIAL_DocDirectory;
- tmpImageName := "_IMAGE_TMP";
- tmpName := Concatenation( path, tmpImageName, ".tex");
- tmpFile := Filename( DirectoryCurrent(), tmpName );
- output := OutputTextFile( tmpFile, false );
- SetPrintFormattingStatus( output, false );
- AppendTo( output,
- "\\nonstopmode\n",
- "\\documentclass{standalone}\n\n",
- __SIMPLICIAL_TikZHeader,
- "\\def\\pgfsysdriver{pgfsys-tex4ht.def}\n\n",
- "\\begin{document}\n");
- AppendTo( output, cont );
- AppendTo( output, "\\end{document}" );
- CloseStream(output);
-
- # Now we remove the leading whitespace
- Exec( "sh -c \" cd ", path,
- "; sed 's/^[ \\t]*//g' -i ",
- Concatenation(tmpImageName, ".tex"), "; \"" );
-
- # Step 2
- # TODO separate the calls to these shell-files into a function and call
- # that. Furthermore, add into the README a short test if a user has all
- # necessary capabilities (and maybe an installation for them)
- Exec( "sh -c \" cd ", path, "; ../flatex/flatex ",
- Concatenation(tmpImageName, ".tex"), "> /dev/null; \"" );
-
- # Step 3
- sysDirPath := DirectoriesSystemPrograms();
- md5 := Filename( sysDirPath, "md5sum" );
- if md5 = fail then
- Error("There is no md5sum installed.");
- fi;
-
- inStream := InputTextNone();
- out := "";
- outStream := OutputTextString(out, true);
- Process( Directory(path), md5, inStream, outStream, [ Concatenation(tmpImageName, ".flt") ] );
- CloseStream(inStream);
- CloseStream(outStream);
- hash := SplitString( out, " " )[1];
-
- # Step 4
- name := Concatenation( "_IMAGE_", hash );
- if not IsExistingFile(
- Filename( DirectoryCurrent(),
- Concatenation( path, name, ".tex" ) ) ) or
- not IsExistingFile(
- Filename( DirectoryCurrent(),
- Concatenation( path, name, "-1.svg" )) ) or
- not IsExistingFile(
- Filename( DirectoryCurrent(),
- Concatenation( path, name, ".pdf" )) ) then
- # Either tex or svg or pdf are not there. We will write them now.
-
- # Step 5
- Exec( "sh -c \" cd ", path, "; mv _IMAGE_TMP.tex ", Concatenation(name, ".tex"), "; \" " );
-
- # Step 6
- Exec( "sh -c \" cd ", path, "; pdflatex -halt-on-error ", Concatenation(name, ".tex"), "; \" " );
- Exec( "sh -c \" cd ", path, "; htlatex ", Concatenation(name, ".tex"), "; \" " );
-
- # After compiling we do some post-processing on the image.
- # We will modify
- # - subscripts, since baseline-shift is not supported in Firefox
- # (see https://stackoverflow.com/questions/12332448/subscripts-and-superscripts-in-svg)
- # As this bug was recognized 16 years ago it is unlikely to
- # change in the near future. We therefore apply the suggested
- # fix in the stackoverflow-answer.
- # WARNING: I am just messing around on the assumption that all
- # subscripts have the same height. If that turns out to be
- # wrong, you have to think of a more sophisticated mechanism.
- Exec( "sh -c \" cd ", path,
- "; sed 's/baseline-shift=\\\"sub\\\"/dy=\\\"3\\\"/g' -i ",
- Concatenation(name, "-1.svg"), ";\" ");
- # I do not really understand why there has to be so much
- # escaping but it does not work with less.
- fi;
-
- # Step 7
- Add( __SIMPLICIAL_ImageNames, name);
-
- # Step 8 will be done in the htmlString below
-
-
- # Inclusion in the LaTeX-version is centered
- latexString := Concatenation( "\n\\begin{center}\n",
- "\\includegraphics{", name, ".pdf}\n\\end{center}\n" );
- n1 := ParseTreeXMLString(latexString);
- n1.name := "Alt";
- n1.attributes.Only := "LaTeX";
-
- # To include it in the HTML-version we have to use a different node
- htmlString := Concatenation(
- "
]]>");
- n2 := ParseTreeXMLString(htmlString);
- n2.name := "Alt";
- n2.attributes.Only := "HTML";
-
-
- # Generate the text version
- consoleString := "\n[an image that is not shown in text version. ";
- consoleString := Concatenation( consoleString, manualposition);
- consoleString := Concatenation( consoleString, "]\n");
- n3 := ParseTreeXMLString(consoleString);
- n3.name := "Alt";
- n3.attributes.Only := "Text";
-
-
- # Replace this node by the new nodes
- node.content := [n1,n2,n3];
- node.attributes.Only := "HTML,LaTeX,Text";
- fi;
-end;
-
-
-
-# Now we have the XML-tree of the documentation
-# We need to change the -Tags into proper GAPDoc tags
-# For that we define a function that changes one node
-preProcessJavaScript := function( node )
- local cont, name, path, n1, n2, n3, consoleString, htmlString, latexString, tmpImageName, tmpName;
-
- if node.name = "Alt" and IsBound(node.attributes.Only) and
- node.attributes.Only in ["JavaScript","Javascript"] then
-
- # get the content of the tag (we need to remove whitespaces)
- cont := GetTextXMLTree(node);
- cont := ReplacedString( cont, " ", "" );
- cont := ReplacedString( cont, "\n", "" );
- cont := ReplacedString( cont, ".html", "" );
- path := __SIMPLICIAL_DocDirectory;
- tmpImageName := "_IMAGE_TMP";
- tmpName := Concatenation( path, tmpImageName, ".tex");
- PrintTo("doc/Test.js", "var page = require('webpage').create();\n");
- AppendTo( "doc/Test.js", "page.viewportSize = { width: 1920, height: 1080 };\n");
- AppendTo( "doc/Test.js", "page.clipRect = { top: 400, left: 750, width: 375, height: 300 };\n");
- AppendTo( "doc/Test.js", Concatenation("page.open('doc/", cont, ".html', function() {\n") );
- AppendTo( "doc/Test.js", Concatenation("page.render('doc/", cont, ".png');\n") );
- AppendTo( "doc/Test.js", "phantom.exit();\n" );
- AppendTo( "doc/Test.js", "});" );
- Exec("./doc/phantomjs /doc/Test.js");
- name := cont;
-
-
-
- Add( __SIMPLICIAL_ImageNames, name);
-
-
-
-
- # Inclusion in the LaTeX-version is centered
- latexString := Concatenation( "\n\\begin{center}\n", "\\includegraphics{", name, ".png}\n\\end{center}\n" );
- n1 := ParseTreeXMLString(latexString);
- n1.name := "Alt";
- n1.attributes.Only := "LaTeX";
-
- # To include it in the HTML-version we have to use a different node
- htmlString := Concatenation(
- "]]>");
- n2 := ParseTreeXMLString(htmlString);
- n2.name := "Alt";
- n2.attributes.Only := "HTML";
-
-
- # Generate the text version
- consoleString := "\n[an image that is not shown in text version]\n";
- n3 := ParseTreeXMLString(consoleString);
- n3.name := "Alt";
- n3.attributes.Only := "Text";
-
-
- # Replace this node by the new nodes
- node.content := [n1,n2,n3];
- node.attributes.Only := "HTML,LaTeX,Text";
-
- fi;
-end;
-
-
-
-BindGlobal( "CleanImageDirectory", function( )
- local allFiles, file;
-
- # First we remove the temporary files
- Exec( "sh -c \" cd ", __SIMPLICIAL_DocDirectory, "; rm -f _IMAGE_TMP*;\"" );
-
- # Secondly we remove all old image files
- allFiles := DirectoryContents( __SIMPLICIAL_DocDirectory );
- for file in allFiles do
- # We only do something with temporary image files
- if StartsWith( file, "_IMAGE_" ) then
- if ForAny( __SIMPLICIAL_ImageNames, n -> StartsWith(file,n) ) then
- # This is a file to an existing picture
- if not ForAny( [".tex", ".svg", ".pdf"], e -> EndsWith(file,e) ) then
- # Does not end in one of those file extensions
- Exec( "sh -c \" cd ", __SIMPLICIAL_DocDirectory, "; rm -f ", file, ";\"" );
- fi;
- else
- # This is an old file that can be removed
- Exec( "sh -c \" cd ", __SIMPLICIAL_DocDirectory, "; rm -f ", file, ";\"" );
- fi;
- fi;
- od;
-end
-);
-
-# We now want to put this preprocessing within the MakeGAPDocDoc-function.
-# To do so we have to redefine the original global variable and add our
-# method into the code. All code that is not commented with MB is original
-# GAPDoc-code.
-
-MakeReadWriteGlobal("MakeGAPDocDoc");
-UnbindGlobal("MakeGAPDocDoc"); # MB unbind this to modify it
-BindGlobal("MakeGAPDocDoc", function(path, main, files, bookname, opts...)
- local special, gaproot, str, r, t, l, latex, null, log, pos, h, i, j;
- special := Filtered(opts, a-> a in ["MathML", "Tth", "MathJax", "nopdf"]);
- if Length(special) > 0 then
- opts := Filtered(opts, a-> not a in ["MathML", "Tth", "MathJax", "nopdf"]);
- fi;
- if IsBound(opts[1]) then
- gaproot := opts[1];
- else
- gaproot := false;
- fi;
- # ensure that path is directory object
- if IsString(path) then
- path := Directory(path);
- fi;
- # ensure that .xml is stripped from name of main file
- if Length(main)>3 and main{[Length(main)-3..Length(main)]} = ".xml" then
- main := main{[1..Length(main)-4]};
- fi;
- # compose the XML document
- Info(InfoGAPDoc, 1, "#I Composing XML document . . .\n");
- str := ComposedDocument("GAPDoc", path,
- Concatenation(main, ".xml"), files, true);
- # parse the XML document
- Info(InfoGAPDoc, 1, "#I Parsing XML document . . .\n");
- r := ParseTreeXMLString(str[1], str[2]);
-
- #MB precompile the images
- Exec( "sh -c \" cd ", __SIMPLICIAL_DocDirectory, "; rm -f _TIKZ_*;\"" );
- __SIMPLICIAL_MANUAL_MODE := true;
- Read("gap/PolygonalComplexes/drawing.gd");
- Read("gap/Library/library_images.gd");
- Read("gap/PolygonalComplexes/distances_images.gd");
- Read("gap/PolygonalComplexes/animating_images.gd");
- Read("gap/ColouredComplexes/edgeColouring_images.gd");
- Read("gap/ColouredComplexes/variColouring_images.gd");
- Read("gap/ColouredComplexes/isoscelesColouring_images.gd");
- Read("gap/Flags/flags_images.gd");
- __SIMPLICIAL_MANUAL_MODE := false;
- # Fortunately there already is a method to apply this function to all nodes of the tree
- ApplyToNodesParseTree( r, preProcessTikz );
- ApplyToNodesParseTree( r, preProcessJavaScript );
-
- CleanImageDirectory();
-
- # clean the result
- Info(InfoGAPDoc, 1, "#I Checking XML structure . . .\n");
- CheckAndCleanGapDocTree(r);
- # produce text version
- Info(InfoGAPDoc, 1,
- "#I Text version (also produces labels for hyperlinks):\n");
- t := GAPDoc2Text(r, path);
- GAPDoc2TextPrintTextFiles(t, path);
- # produce LaTeX version
- Info(InfoGAPDoc, 1, "#I Constructing LaTeX version and calling pdflatex:\n");
- r.bibpath := path;
- l := GAPDoc2LaTeX(r);
- Info(InfoGAPDoc, 1, "#I Writing LaTeX file, \c");
- Info(InfoGAPDoc, 2, Concatenation(main, ".tex"), "\n#I ");
- FileString(Filename(path, Concatenation(main, ".tex")), l);
- if "nopdf" in special then
- Info(InfoGAPDoc, 1, " NO PDF generated!\n");
- PrintSixFile(Filename(path, "manual.six"), r, bookname);
- else
- if Filename(DirectoriesSystemPrograms(), "pdflatex") = fail then
- Info(InfoGAPDoc, 1, "\n#W WARNING: cannot find 'pdflatex', please install TeX.\n");
- Info(InfoGAPDoc, 1, "#W WARNING: will NOT produce pdf version from LaTeX file.\n");
- else
- # call latex and pdflatex (with bibtex, makeindex and dvips)
- latex := "latex -interaction=nonstopmode ";
- # sh-syntax for redirecting stderr and stdout to /dev/null
- null := " > /dev/null 2>&1 ";
- Info(InfoGAPDoc, 1, "4 x pdflatex with bibtex and makeindex, \c");
- Exec(Concatenation("sh -c \" cd ", Filename(path,""),
- "; rm -f ", main, ".aux ", main, ".pdf ", main, ".log ",
- "; pdf", latex, main, null,
- "; bibtex ", main, null,
- "; pdf", latex, main, null,
- "; makeindex ", main, null,
- "; pdf", latex, main, null,
- "; pdf", latex, main, null,"\""));
- # check log file for errors, warning, overfull boxes
- log := Filename(path, Concatenation(main, ".log"));
- log := StringFile(log);
- if log = fail then
- Info(InfoGAPDoc, 1, "\n#W WARNING: Something wrong, don't find log file ",
- Filename(path, Concatenation(main, ".log")), "\n");
- else
- log := SplitString(log, "\n", "");
- pos := Filtered([1..Length(log)], i-> Length(log[i]) > 0
- and log[i][1] = '!');
- if Length(pos) > 0 then
- Info(InfoGAPDoc, 1, "\n#W There were LaTeX errors:\n");
- for i in pos do
- for j in [i..Minimum(i+2, Length(log))] do
- Info(InfoGAPDoc, 1, log[j], "\n");
- od;
- Info(InfoGAPDoc, 1, "____________________\n");
- od;
- fi;
- pos := Filtered([1..Length(log)], i-> Length(log[i]) > 13
- and log[i]{[1..14]} = "LaTeX Warning:");
- if Length(pos) > 0 then
- Info(InfoGAPDoc, 1, "\n#W There were LaTeX Warnings:\n");
- for i in pos do
- for j in [i..Minimum(i+2, Length(log))] do
- Info(InfoGAPDoc, 1, log[j], "\n");
- od;
- Info(InfoGAPDoc, 1, "____________________\n");
- od;
- fi;
- pos := Filtered([1..Length(log)], i-> Length(log[i]) > 7
- and log[i]{[1..8]} = "Overfull");
- if Length(pos) > 0 then
- Info(InfoGAPDoc, 1, "\n#W There are overfull boxes:\n");
- for i in pos do
- Info(InfoGAPDoc, 1, log[i], "\n");
- od;
- fi;
- fi;
- # check for BibTeX warnings
- log := StringFile(Filename(path, Concatenation(main, ".blg")));
- if log <> fail then
- log := SplitString(log, "\n", "");
- log := Filtered(log, z-> PositionSublist(z, "Warning--") = 1);
- if Length(log) > 0 then
- Info(InfoGAPDoc, 1, "\n#W BibTeX had warnings:\n",
- JoinStringsWithSeparator(log, "\n"));
- fi;
- fi;
-
- if not IsExistingFile(Filename(path, Concatenation(main, ".pdf"))) then
- Info(InfoGAPDoc, 1, "\n#I ERROR: no .pdf file produced (writing incomplete .six file)\n");
- PrintSixFile(Filename(path, "manual.six"), r, bookname);
- else
- Exec(Concatenation("sh -c \" cd ", Filename(path,""),
- "; mv ", main, ".pdf manual.pdf; ",
- "\""));
- Info(InfoGAPDoc, 1, "\n");
- # read page number information for .six file
- Info(InfoGAPDoc, 1, "#I Writing manual.six file ... \c");
- Info(InfoGAPDoc, 2, Filename(path, "manual.six"), "\n");
- Info(InfoGAPDoc, 1, "\n");
- AddPageNumbersToSix(r, Filename(path, Concatenation(main, ".pnr")));
- # print manual.six file
- PrintSixFile(Filename(path, "manual.six"), r, bookname);
- fi;
- fi;
- fi;
- # produce html version
- Info(InfoGAPDoc, 1, "#I Finally the HTML version . . .\n");
- # if MathJax version is also produced we include links to them
- if "MathJax" in special then
- r.LinkToMathJax := true;
- fi;
- h := GAPDoc2HTML(r, path, gaproot);
- GAPDoc2HTMLPrintHTMLFiles(h, path);
- Unbind(r.LinkToMathJax);
- if "Tth" in special then
- Info(InfoGAPDoc, 1,
- "#I - also HTML version with 'tth' translated formulae . . .\n");
- h := GAPDoc2HTML(r, path, gaproot, "Tth");
- GAPDoc2HTMLPrintHTMLFiles(h, path);
- fi;
- if "MathML" in special then
- Info(InfoGAPDoc, 1, "#I - also HTML + MathML version with 'ttm' . . .\n");
- h := GAPDoc2HTML(r, path, gaproot, "MathML");
- GAPDoc2HTMLPrintHTMLFiles(h, path);
- fi;
- if "MathJax" in special then
- Info(InfoGAPDoc, 1, "#I - also HTML version for MathJax . . .\n");
- h := GAPDoc2HTML(r, path, gaproot, "MathJax");
- GAPDoc2HTMLPrintHTMLFiles(h, path);
- fi;
-
- return r;
-end);
-
# Create binary and index files for the library
__SIMPLICIAL_LibraryConstructBinary();
__SIMPLICIAL_LibraryConstructIndex();
-# After all this preparatory work we can finally call the function to create
-# the documentation
-
AutoDoc( rec( scaffold := rec(
MainPage := false),
- dir := __SIMPLICIAL_DocDirectory,
+ dir := "doc/",
extract_examples := true,
autodoc := rec(
files := [ ],
scan_dirs := ["doc", "gap", "gap/PolygonalComplexes", "gap/Paths", "gap/Library", "gap/ColouredComplexes", "gap/Flags", "gap/Morphisms"]),
gapdoc := rec(
files := ["doc/PolygonalStructuresDefinitions.xml", "doc/ExampleImplementations.xml"],
- LaTeXOptions := rec( LateExtraPreamble := __SIMPLICIAL_TikZHeader )
+ LaTeXOptions := rec( LateExtraPreamble := "\\input{TikZHeader.tex}\n\n" )
))
);
diff --git a/unit_tests/Test_Graphs.g b/unit_tests/Test_Graphs.g
index e1b691a4..0fc0cbe1 100644
--- a/unit_tests/Test_Graphs.g
+++ b/unit_tests/Test_Graphs.g
@@ -61,26 +61,29 @@ if IsPackageMarkedForLoading( "Digraphs", ">=0.10.1" ) then
od;
Assert(0, Set(DigraphEdges(digButterfly))=Set(Filtered(Union(FacesOfEdges(butterfly),reversedButterfly),i->Length(i)=2)));
end);
+fi;
+if IsPackageMarkedForLoading( "Digraphs", ">=1.9.0" ) then
BindGlobal( "__SIMPLICIAL_Test_AllSimplicialSurfacesOfDigraph", function()
local dig, surface, list1, list2;
surface:=SimplicialSurfaceByVerticesInFaces([[1,4,5],[1,4,6],[1,5,7],[1,6,7],[2,3,5],[2,3,6],[2,4,5],[2,4,6],[3,5,7],[3,6,7]]);
dig:=FaceDigraphsGraph(surface);
- list1:=AllSimplicialSurfacesOfDigraph(dig,true);
- Assert(0,Length(list1)=1);
- Assert(0,IsIsomorphic(list1[1],surface));
-
+
list2:=AllSimplicialSurfacesOfDigraph(dig);
Assert(0, Length(Filtered(list2,IsVertexFaithful))=1);
Assert(0,IsIsomorphic(Filtered(list2,IsVertexFaithful)[1],surface));
Assert(0,Length(list2)=11);
+ list1:=AllSimplicialSurfacesOfDigraph(dig,true);
+ Assert(0,Length(list1)=1);
+ Assert(0,IsIsomorphic(list1[1],surface));
+
dig:=DigraphByEdges([ [ 1, 2 ], [ 1, 4 ], [ 1, 5 ], [ 2, 1 ], [ 2, 3 ], [ 2, 6 ], [ 3, 2 ], [ 3, 4 ], [ 3, 5 ], [ 4, 1 ], [ 4, 3 ],
[ 4, 6 ], [ 5, 1 ],[ 5, 3 ], [ 5, 6 ], [ 6, 2 ], [ 6, 4 ], [ 6, 5 ] ]);
- list1:=AllSimplicialSurfacesOfDigraph(dig,true);
- list2:=AllSimplicialSurfacesOfDigraph(dig,false);
- Assert(0,Length(list1)=0);
- Assert(0,Length(list2)=2);
+ list1:=AllSimplicialSurfacesOfDigraph(dig,false);
+ list2:=AllSimplicialSurfacesOfDigraph(dig,true);
+ Assert(0,Length(list1)=2);
+ Assert(0,Length(list2)=0);
#One of the smallest face graph with more than one vertex-faithful surface
dig:=DigraphByEdges([ [ 1, 9 ], [ 9, 1 ], [ 1, 13 ], [ 13, 1 ], [ 1, 14 ], [ 14, 1 ], [ 2, 6 ], [ 6, 2 ], [ 2, 7 ], [ 7, 2 ], [ 2, 10 ],
diff --git a/unit_tests/test_main.g b/unit_tests/test_main.g
index 9099608d..6744607b 100644
--- a/unit_tests/test_main.g
+++ b/unit_tests/test_main.g
@@ -61,7 +61,9 @@ BindGlobal( "SIMPLICIAL_TestAll", function()
__SIMPLICIAL_Test_EdgeNautyGraph();
__SIMPLICIAL_Test_FaceDigraphsGraph();
__SIMPLICIAL_Test_FaceNautyGraph();
- __SIMPLICIAL_Test_AllSimplicialSurfacesOfDigraph();
+ if IsPackageMarkedForLoading( "Digraphs", ">=1.9.0" ) then
+ __SIMPLICIAL_Test_AllSimplicialSurfacesOfDigraph();
+ fi;
__SIMPLICIAL_Test_FaceTwoColouring();