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();