diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 5c6ffe49..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,8 +38,13 @@ 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@v3 + - uses: codecov/codecov-action@v4 + with: + token: ${{ secrets.CODECOV_TOKEN }} # The documentation job manual: @@ -49,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" @@ -60,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 94aa820c..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", @@ -105,14 +115,14 @@ AbstractHTML := "The SimplicialSurfaces package PackageDoc := rec( BookName := "SimplicialSurfaces", ArchiveURLSubset := ["doc"], - HTMLStart := "doc/chap0.html", + HTMLStart := "doc/chap0_mj.html", PDFFile := "doc/manual.pdf", SixFile := "doc/manual.six", LongTitle := "Computation with simplicial surfaces and folding processes.", ), 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.gd b/gap/Paths/paths.gd index 4f297de6..da8f801f 100644 --- a/gap/Paths/paths.gd +++ b/gap/Paths/paths.gd @@ -600,13 +600,13 @@ DeclareAttribute( "AssociatedPolygonalComplex", IsVertexEdgePath ); #! #! Image omitted in terminal text #! -#! @BeginExampleSession +#! @BeginLogSession #! gap> butterfly:=SimplicialSurfaceByDownwardIncidence( #! > [[1,2],[1,3],[2,3],[3,4],[1,4]],[[1,2,3],[2,4,5]]);; #! gap> AllClosedVertexEdgePaths(butterfly); #! [ ( v3, E2, v1, E5, v4, E4, v3 ), ( v3, E3, v2, E1, v1, E2, v3 ), #! ( v3, E3, v2, E1, v1, E5, v4, E4, v3 ) ] -#! @EndExampleSession +#! @EndLogSession #! @Returns a list of vertex-edge-paths #! @Arguments complex DeclareOperation( "AllClosedVertexEdgePaths", [IsTwistedPolygonalComplex] ); @@ -1303,10 +1303,10 @@ DeclareAttribute( "AllThreeWaistsOfComplex", IsTwistedPolygonalComplex); #! The definition of a waist is given at the beginning of section . #! #! For example, consider the double tetrahedron from the start of section : -#! @BeginExampleSession +#! @BeginLogSession #! gap> AllWaistsOfComplex(doubleTetra); #! [ ( v4, E10, v5, E8, v3, E7, v4 ), ( v6, E12, v5, E8, v3, E9, v6 ) ] -#! @EndExampleSession +#! @EndLogSession #! #! @Returns a set of closed vertex-edge-paths #! @Arguments complex diff --git a/gap/Paths/paths.gi b/gap/Paths/paths.gi index 5fe355e1..fb2419f0 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 ); 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 ed76b65b..8b168352 100644 --- a/gap/PolygonalComplexes/graphs.gd +++ b/gap/PolygonalComplexes/graphs.gd @@ -441,13 +441,13 @@ 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: diff --git a/gap/utilities.g b/gap/utilities.g index a50a15f2..7329a951 100644 --- a/gap/utilities.g +++ b/gap/utilities.g @@ -46,3 +46,65 @@ BindGlobal( "__SIMPLICIAL_ListToCycle", ); + +############################################################################# +## +#F LexicographicallyLeastDyclet . . . . . . . lexicographically least dyclet +## +## A dyclet represents the orbit of a list under the diherdral group of the +## list. Find the lexicographically least representative of the dyclet. +## + +# called for lists of sets +BindGlobal( "__SIMPLICIAL_LexicographicallyLeastDyclet", + function(dyclet) + + local n, min_dyclet, k, perm, mirror_dyclet; + + if (Filtered(dyclet,i-> not IsInt(i))<>[]) or (Length(dyclet)=0) then + Error("Dyclet must be a list of integers"); + return fail; + fi; + n := Size(dyclet); + # current candidate for minimal dyclet + min_dyclet := ShallowCopy(dyclet); + perm := Reversed(dyclet); + if perm < min_dyclet then min_dyclet := perm; fi; + + # Generate all cyclic permutations and their mirror images + for k in [1..n-1] do + perm := Concatenation(dyclet{[1+k..n]}, dyclet{[1..k]}); + mirror_dyclet := Reversed(perm); + + if perm < min_dyclet then + min_dyclet := perm; + fi; + + if mirror_dyclet < min_dyclet then + min_dyclet := mirror_dyclet; + fi; + od; + + return min_dyclet; +end +); + + +############################################################################# +## +#F LtDyclet . . . . . . . . . . . . less than comparisons of dyclets +## +BindGlobal( "__SIMPLICIAL_LtDyclet", + function( dyc1, dyc2 ) + + if __SIMPLICIAL_LexicographicallyLeastDyclet(dyc1) + < __SIMPLICIAL_LexicographicallyLeastDyclet(dyc2) then return true; + fi; + + return false; + +end +); + + +