From 75b99fa5eb47c53813beea32b2cf41f026648c7e Mon Sep 17 00:00:00 2001 From: "Robert C. Haraway, III" Date: Tue, 20 Oct 2020 16:15:51 -0500 Subject: [PATCH 01/31] Recognition for trivial I-bundle over torus Implemented T^2xI recognition; implemented auxiliary routines to simplify boundary; added unit tests for T^2xI recognition; added Python bindings for T^2xI recognition. --- engine/triangulation/dim3/decompose.cpp | 128 ++++++++++++++++++- engine/triangulation/dim3/triangulation3.h | 35 ++++++ python/dim3/triangulation3.cpp | 2 + testsuite/dim3/triangulation3.cpp | 140 +++++++++++++++++++++ 4 files changed, 304 insertions(+), 1 deletion(-) diff --git a/engine/triangulation/dim3/decompose.cpp b/engine/triangulation/dim3/decompose.cpp index a8af326319..1fd8087092 100644 --- a/engine/triangulation/dim3/decompose.cpp +++ b/engine/triangulation/dim3/decompose.cpp @@ -39,6 +39,9 @@ #include "surfaces/normalsurface.h" #include "surfaces/normalsurfaces.h" #include "triangulation/dim3.h" +#include "triangulation/generic/boundarycomponent.h" +#include "triangulation/generic/face.h" +#include "triangulation/homologicaldata.h" namespace regina { @@ -561,7 +564,7 @@ bool Triangulation<3>::isSolidTorus() const { delete crushed; } } - + bool Triangulation<3>::knowsSolidTorus() const { if (solidTorus_.known()) return true; @@ -587,6 +590,129 @@ bool Triangulation<3>::knowsSolidTorus() const { return false; } + +Edge<3>* Triangulation<3>::getEmbeddedBoundaryEdge() { + for (BoundaryComponent<3> *k : boundaryComponents()) + for (Edge<3> *e : k->edges()) + if (e->vertex(0) != e->vertex(1)) + return e; + return (Edge<3>*) 0; +} + +Edge <3>* Triangulation<3>::getClosableBoundaryEdge() { + for (BoundaryComponent<3> *k : boundaryComponents()) + for (Edge<3> *e : k->edges()) + if (closeBook(e,true,false)) + return e; + return (Edge<3>*) 0; +} + +bool Triangulation<3>::isTorusXInterval() const { + if (knowsTorusXInterval()){ + return torusXInterval_.value(); + } + + Triangulation<3>* working = new Triangulation<3>(*this, false); + working->intelligentSimplify(); + working->idealToFinite(); + working->intelligentSimplify(); + + // If it's not a homology T2xI, we're done. + if (!working->isConnected() + || working->countBoundaryComponents() != 2 + || working->homology().str() != "2 Z" + || working->homologyRel().str() != "Z" + || working->homologyBdry().str() != "4 Z"){ + delete working; + return (torusXInterval_ = false); + } + + // Crushing will not introduce new boundary faces. + // T2xI is irreducible, so we should be able to crush to a 0-efficient triangulation. + + int surfs = 0; + for (NormalSurface* round = working->hasNonTrivialSphereOrDisc(); + round; + round = working->hasNonTrivialSphereOrDisc()){ + + Triangulation<3>* crushed = round->crush(); + delete round; + delete working; + working = 0; + + int n = crushed->splitIntoComponents(); + if (n == 0){ + // A round surface crushed everything away; we don't have T2xI. + delete crushed; + return (torusXInterval_ = false); + } + + bool gotNewTriangulation = false; + for (Packet* p = crushed->firstChild(); p; p->nextSibling()){ + working = static_cast*>(p); + // If it's a homology T2xI, use it. + // (We can't have two such components, + // since then the original triangulation would not have been a homology T2xI.) + if (working->isConnected() + && working->countBoundaryComponents() == 2 + && working->homology().str() == "2 Z" + && working->homologyRel().str() == "Z" + && working->homologyBdry().str() == "4 Z"){ + // Save our work into a new triangulation before cleaning up crushed. + working = new Triangulation<3>(*working, false); + delete crushed; + // The above deletion presumably deletes crushing's descendants too, + // such as p and working's former value. + // See similar code in isSolidTorus. + gotNewTriangulation = true; + break; + } + } + + if (!gotNewTriangulation){ + // None of the components of the crushing was a homology T2xI. + if (working) + delete working; + return (torusXInterval_ = false); + } + } + + // At this point we should already have boundary components with one vertex each. + // But out of an abundance of caution, we ensure this is the case. + Edge<3>* emb = working->getEmbeddedBoundaryEdge(); + while (emb != 0){ + working->layerOn(emb); + Edge<3>* cls = working->getClosableBoundaryEdge(); + while (cls != 0){ + working->closeBook(cls,false,true); + cls = working->getClosableBoundaryEdge(); + } + emb = working->getEmbeddedBoundaryEdge(); + } + + // We have a 0-efficient homology T2xI. + // So we should move on to the meat of the algorithm, testing Dehn fillings. + + BoundaryComponent<3>* k = working->boundaryComponent(0); + bool isT2xI = true; + for (Edge<3>* e : k->edges()){ + Triangulation<3>* filled = new Triangulation<3>(*working, false); + Edge<3>* cls = filled->edge(e->index()); + filled->closeBook(cls, false, true); + isT2xI = isT2xI && filled->isSolidTorus(); + delete filled; + if (!isT2xI) { + break; + } + } + delete working; + return (torusXInterval_ = isT2xI); +} + +bool Triangulation<3>::knowsTorusXInterval() const { + return torusXInterval_.known(); +} + Packet* Triangulation<3>::makeZeroEfficient() { // Extract a connected sum decomposition. Container* connSum = new Container(); diff --git a/engine/triangulation/dim3/triangulation3.h b/engine/triangulation/dim3/triangulation3.h index c1334c2fca..24310be51f 100644 --- a/engine/triangulation/dim3/triangulation3.h +++ b/engine/triangulation/dim3/triangulation3.h @@ -163,6 +163,8 @@ class REGINA_API Triangulation<3> : /**< Is this a triangulation of a 3-dimensional ball? */ mutable Property solidTorus_; /**< Is this a triangulation of the solid torus? */ + mutable Property torusXInterval_; + /**< Is this a triangulation of the solid torus? */ mutable Property irreducible_; /**< Is this 3-manifold irreducible? */ mutable Property compressingDisc_; @@ -2036,6 +2038,39 @@ class REGINA_API Triangulation<3> : */ bool knowsSolidTorus() const; + /** + * Return a pointer to an embedded boundary edge. + */ + + Edge<3> *getEmbeddedBoundaryEdge(); + + /** + * Return a pointer to a closable boundary edge. + */ + + Edge<3> *getClosableBoundaryEdge(); + + /** + * Determines whether or not the underlying 3-manifold is + * the product of a torus with an interval, + * using the algorithm from + * https://journals.carleton.ca/jocg/index.php/jocg/article/view/433 + * (also available as arXiv:1410.7115). + * + * \warning This algorithm ultimately relies on isSolidTorus(), + * which might run slowly for large triangulations. + * + * \pre This triangulation induces one-vertex triangulations + * on its boundary components. + */ + bool isTorusXInterval() const; + + /** + * Determines whether or not the value of isTorusXInterval is known, + * not the value itself. + */ + bool knowsTorusXInterval() const; + /** * Determines whether the underlying 3-manifold (which must be * closed) is irreducible. In other words, this routine determines diff --git a/python/dim3/triangulation3.cpp b/python/dim3/triangulation3.cpp index 04de3e2da4..0e0e266d6f 100644 --- a/python/dim3/triangulation3.cpp +++ b/python/dim3/triangulation3.cpp @@ -296,6 +296,8 @@ void addTriangulation3(pybind11::module& m) { .def("knowsBall", &Triangulation<3>::knowsBall) .def("isSolidTorus", &Triangulation<3>::isSolidTorus) .def("knowsSolidTorus", &Triangulation<3>::knowsSolidTorus) + .def("isTorusXInterval", &Triangulation<3>::isTorusXInterval) + .def("knowsTorusXInterval", &Triangulation<3>::knowsTorusXInterval) .def("isIrreducible", &Triangulation<3>::isIrreducible) .def("knowsIrreducible", &Triangulation<3>::knowsIrreducible) .def("hasCompressingDisc", &Triangulation<3>::hasCompressingDisc) diff --git a/testsuite/dim3/triangulation3.cpp b/testsuite/dim3/triangulation3.cpp index 8c13394f6c..2129a260f0 100644 --- a/testsuite/dim3/triangulation3.cpp +++ b/testsuite/dim3/triangulation3.cpp @@ -99,6 +99,7 @@ class Triangulation3Test : public TriangulationTest<3> { CPPUNIT_TEST(threeSphereRecognitionLarge); CPPUNIT_TEST(threeBallRecognition); CPPUNIT_TEST(solidTorusRecognition); + CPPUNIT_TEST(torusXIntervalRecognition); CPPUNIT_TEST(turaevViro); CPPUNIT_TEST(barycentricSubdivision); CPPUNIT_TEST(idealToFinite); @@ -2866,6 +2867,145 @@ class Triangulation3Test : public TriangulationTest<3> { runCensusAllBounded(&testSolidTorus4); } + Triangulation<3>* verifyTorusXInterval(Triangulation<3>* tri, + const char* triName = 0) { + if (triName) + tri->setLabel(triName); + + Triangulation<3> bounded(*tri); + if (bounded.isIdeal()) + bounded.idealToFinite(); + clearProperties(bounded); + + Triangulation<3> ideal(*tri); + if (ideal.hasBoundaryTriangles()) + ideal.finiteToIdeal(); + clearProperties(ideal); + + Triangulation<3> boundedBig(bounded); + boundedBig.barycentricSubdivision(); + clearProperties(boundedBig); + + Triangulation<3> idealBig(ideal); + idealBig.barycentricSubdivision(); + clearProperties(idealBig); + + if (! bounded.isTorusXInterval()) { + CPPUNIT_FAIL(("The real T^2xI " + + tri->label() + " is not recognised as such.").c_str()); + } + if (! ideal.isTorusXInterval()) { + CPPUNIT_FAIL(("The ideal T^2xI " + + tri->label() + " is not recognised as such.").c_str()); + } + if (! boundedBig.isTorusXInterval()) { + CPPUNIT_FAIL(("The subdivided real T^2xI " + + tri->label() + " is not recognised as such.").c_str()); + } + if (! idealBig.isTorusXInterval()) { + CPPUNIT_FAIL(("The subdivided ideal T^2xI " + + tri->label() + " is not recognised as such.").c_str()); + } + + return tri; + } + + Triangulation<3>* verifyNotTorusXInterval(Triangulation<3>* tri, + const char* triName = 0) { + if (triName) + tri->setLabel(triName); + + Triangulation<3> bounded(*tri); + if (bounded.isIdeal()) + bounded.idealToFinite(); + clearProperties(bounded); + + Triangulation<3> ideal(*tri); + if (ideal.hasBoundaryTriangles()) + ideal.finiteToIdeal(); + clearProperties(ideal); + + Triangulation<3> boundedBig(bounded); + boundedBig.barycentricSubdivision(); + clearProperties(boundedBig); + + Triangulation<3> idealBig(ideal); + idealBig.barycentricSubdivision(); + clearProperties(idealBig); + + if (bounded.isTorusXInterval()) { + CPPUNIT_FAIL(("The real non-T^2xI " + + tri->label() + " is recognised as a T^2xI.").c_str()); + } + if (ideal.isTorusXInterval()) { + CPPUNIT_FAIL(("The ideal non-T^2xI " + + tri->label() + " is recognised as a T^2xI.").c_str()); + } + if (boundedBig.isTorusXInterval()) { + CPPUNIT_FAIL(("The subdivided real non-T^2xI " + + tri->label() + " is recognised as a T^2xI.").c_str()); + } + if (idealBig.isTorusXInterval()) { + CPPUNIT_FAIL(("The subdivided ideal non-T^2xI " + + tri->label() + " is recognised as a T^2xI.").c_str()); + } + + return tri; + } + + void verifyIsoSigTorusXInterval(const std::string& sigStr) { + Triangulation<3>* t = Triangulation<3>::fromIsoSig(sigStr); + t->setLabel(sigStr); + delete verifyTorusXInterval(t); + } + + void verifyIsoSigNotTorusXInterval(const std::string& sigStr) { + Triangulation<3>* t = Triangulation<3>::fromIsoSig(sigStr); + t->setLabel(sigStr); + delete verifyNotTorusXInterval(t); + } + + void torusXIntervalRecognition() { + Triangulation<3>* tri; + + tri = Triangulation<3>::fromIsoSig("eLAkbbcddadbdb"); + delete verifyTorusXInterval(tri, "Ideal T2xI eLAkbbcddadbdb"); + + tri = new Triangulation<3>(); + delete verifyNotTorusXInterval(tri, "Empty triangulation"); + + tri = new Triangulation<3>(); + tri->newTetrahedron(); + delete verifyNotTorusXInterval(tri, "Single tetrahedron"); + + tri = new Triangulation<3>(); + Tetrahedron<3>* tet = tri->newTetrahedron(); + tet->join(0, tet, Perm<4>(3, 1, 2, 0)); + delete verifyNotTorusXInterval(tri, "Snapped tetrahedron"); + + // Now we check some homology-T2xI manifolds. + + // Some links from 4^2_1 thru 7^2_8 + // in Bailey and Roth's tables from Rolfsen's *Knots and links.* + // (5^2_1 and 7^2_8 have the same exterior.) + + verifyIsoSigNotTorusXInterval("eLPkbdcddabgbg"); + verifyIsoSigNotTorusXInterval("eLPkbdcddhgggb"); + verifyIsoSigNotTorusXInterval("eLMkbcdddaeeda"); + verifyIsoSigNotTorusXInterval("eLMkbcddddedde"); + // verifyIsoSigNotTorusXInterval("gLLMQbcdefffmvftaog"); + // verifyIsoSigNotTorusXInterval("fLLQcbecdeepuwsua"); + // verifyIsoSigNotTorusXInterval("hLLAPkbcdefgggtsfxjjgb"); + // verifyIsoSigNotTorusXInterval("hLLMPkbcdfggfgmvfafwkf"); + // verifyIsoSigNotTorusXInterval("hLLzQkcdegffgguvuqpgvk"); + // verifyIsoSigNotTorusXInterval("iLLLAQccdegfhhghdcltautwa"); + // verifyIsoSigNotTorusXInterval("kLLLALQkceffehijjijiiealshealf"); + verifyIsoSigNotTorusXInterval("eLPkbdcddabobv"); + + // Finally, the connected sum of the Poincare homology sphere and T2xI: + // verifyIsoSigNotTorusXInterval("pLvwwLuPIIIkaddkomnjlllonobabtlqinfjwjnw"); + } + void verifyTV3(Triangulation<3>& t, const std::string& triName) { // Verify the Turaev-Viro invariants for r=3. // The expected values are described in the paper of Turaev From d5a77dc31502b68ff927a866001d46e620eac623 Mon Sep 17 00:00:00 2001 From: "Robert C. Haraway, III" Date: Wed, 21 Oct 2020 15:55:22 -0500 Subject: [PATCH 02/31] Implemented scaffolding for fault finding Implemented CMake and CppUnit scaffolding for fault finding; implemented naive separation testing for surfaces. --- engine/surfaces/CMakeLists.txt | 1 + engine/surfaces/fault.cpp | 59 +++++++++ engine/surfaces/normalsurface.h | 17 +++ testsuite/surfaces/CMakeLists.txt | 1 + testsuite/surfaces/fault.cpp | 200 ++++++++++++++++++++++++++++++ testsuite/surfaces/testsurfaces.h | 1 + testsuite/testsuite.cpp | 1 + 7 files changed, 280 insertions(+) create mode 100644 engine/surfaces/fault.cpp create mode 100644 testsuite/surfaces/fault.cpp diff --git a/engine/surfaces/CMakeLists.txt b/engine/surfaces/CMakeLists.txt index 2643812f46..58e9d7f291 100644 --- a/engine/surfaces/CMakeLists.txt +++ b/engine/surfaces/CMakeLists.txt @@ -10,6 +10,7 @@ SET ( FILES disctype enumerator enumfilter + fault links normalsurface normalsurfaces diff --git a/engine/surfaces/fault.cpp b/engine/surfaces/fault.cpp new file mode 100644 index 0000000000..684e487910 --- /dev/null +++ b/engine/surfaces/fault.cpp @@ -0,0 +1,59 @@ + +/************************************************************************** + * * + * Regina - A Normal Surface Theory Calculator * + * Computational Engine * + * * + * This file: Copyright (c) 2020, Robert C. Haraway, III * + * For further details contact Robert (bobbycyiii@fastmail.com). * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * As an exception, when this program is distributed through (i) the * + * App Store by Apple Inc.; (ii) the Mac App Store by Apple Inc.; or * + * (iii) Google Play by Google Inc., then that store may impose any * + * digital rights management, device limits and/or redistribution * + * restrictions that are required by its terms of service. * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * General Public License for more details. * + * * + * You should have received a copy of the GNU General Public * + * License along with this program; if not, write to the Free * + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + * * + **************************************************************************/ + + +#include "surfaces/normalsurface.h" +#include "triangulation/dim3.h" + +namespace regina { + bool NormalSurface::separates() const{ + int tri_cpts = this->triangulation()->countComponents(); + Triangulation<3>* cut_up = this->cutAlong(); + cut_up->intelligentSimplify(); + int new_cpts = cut_up->countComponents(); + delete cut_up; + return tri_cpts < new_cpts; + } + + bool NormalSurface::isEssentialSphere() const{ + return false; + } + + bool NormalSurface::isEssentialTorus() const{ + return false; + } + + bool NormalSurface::isSolidTorusAnnulus() const{ + return false; + } + +} diff --git a/engine/surfaces/normalsurface.h b/engine/surfaces/normalsurface.h index 98fc4098c3..155e053540 100644 --- a/engine/surfaces/normalsurface.h +++ b/engine/surfaces/normalsurface.h @@ -1443,6 +1443,23 @@ class REGINA_API NormalSurface : public ShortOutput { */ bool isIncompressible() const; + /** + * Determines whether or not cutting along the surface + * yields a new component. + */ + bool separates() const; + + bool isEssentialSphere() const; + + bool isEssentialTorus() const; + + /** + * Determines whether or not this surface is an annulus + * cutting along which yields two solid tori. + * This is easier to check than whether or not the annulus is essential. + */ + bool isSolidTorusAnnulus() const; + /** * Cuts the associated triangulation along this surface and * returns a newly created resulting triangulation. diff --git a/testsuite/surfaces/CMakeLists.txt b/testsuite/surfaces/CMakeLists.txt index 15b6922116..d9f5d7013e 100644 --- a/testsuite/surfaces/CMakeLists.txt +++ b/testsuite/surfaces/CMakeLists.txt @@ -2,6 +2,7 @@ # Files to compile SET ( FILES + fault incompressible normalsurfaces ) diff --git a/testsuite/surfaces/fault.cpp b/testsuite/surfaces/fault.cpp new file mode 100644 index 0000000000..2c1c80cdfe --- /dev/null +++ b/testsuite/surfaces/fault.cpp @@ -0,0 +1,200 @@ + +/************************************************************************** + * * + * Regina - A Normal Surface Theory Calculator * + * Test Suite * + * * + * Copyright (c) 2020, Robert C. Haraway, III. * + * For further details contact Robert Haraway (bobbycyiii@fastmail.com). * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * As an exception, when this program is distributed through (i) the * + * App Store by Apple Inc.; (ii) the Mac App Store by Apple Inc.; or * + * (iii) Google Play by Google Inc., then that store may impose any * + * digital rights management, device limits and/or redistribution * + * restrictions that are required by its terms of service. * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * General Public License for more details. * + * * + * You should have received a copy of the GNU General Public * + * License along with this program; if not, write to the Free * + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + * * + **************************************************************************/ + +#include +#include "surfaces/normalsurface.h" +#include "surfaces/normalsurfaces.h" +#include "triangulation/dim3.h" +#include "triangulation/example3.h" +#include "testsuite/dim3/testtriangulation.h" + + +using regina::Example; +using regina::NormalSurfaces; +using regina::Triangulation; + +class FaultTest : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(FaultTest); + + CPPUNIT_TEST(separates); + CPPUNIT_TEST(isEssentialSphere); + CPPUNIT_TEST(isEssentialTorus); + CPPUNIT_TEST(isSolidTorusAnnulus); + + CPPUNIT_TEST_SUITE_END(); + +public: + void setUp() { + } + + void tearDown() { + } + + Triangulation<3>* verifyAllSeparating(Triangulation<3>* tri, + const std::string& triName) { + NormalSurfaces* s = NormalSurfaces::enumerate( + tri, regina::NS_STANDARD, regina::NS_EMBEDDED_ONLY); + bool nonseparating_known = false; + for (unsigned i = 0; i < s->size(); ++i) + if (!(s->surface(i)->separates())){ + nonseparating_known = true; + break; + } + delete s; + if (nonseparating_known) + CPPUNIT_FAIL(("A surface in " + triName + + " is computed to be nonseparating.").c_str()); + return tri; + } + + Triangulation<3>* verifyHasNonSeparating(Triangulation<3>* tri, + const std::string& triName){ + NormalSurfaces* s = NormalSurfaces::enumerate( + tri, regina::NS_STANDARD, regina::NS_EMBEDDED_ONLY); + bool all_known_surfaces_separating = true; + for (unsigned i = 0; i < s->size(); ++i) + if (!(s->surface(i)->separates())){ + all_known_surfaces_separating = false; + break; + } + delete s; + if (all_known_surfaces_separating) + CPPUNIT_FAIL(("No surfaces in " + triName + + " were computed to be nonseparating.").c_str()); + return tri; + } + + void separates() { + Triangulation<3>* tri; + + // Manifolds without nonseparating surfaces + + tri = Example<3>::threeSphere(); + delete verifyAllSeparating(tri, "Minimal 3-sphere"); + + tri = Example<3>::simplicialSphere(); + delete verifyAllSeparating(tri, "Pentachoron boundary 3-sphere"); + + tri = Example<3>::ball(); + delete verifyAllSeparating(tri, "One-tetrahedron ball"); + + int p = 3; + int q = 2; + while (p < 1000){ + if (p % 2 != 0){ + tri = Example<3>::lens(p,q); + delete verifyAllSeparating(tri, "Lens space with odd p"); + } + p = p + q; + q = p - q; + } + + tri = Example<3>::poincareHomologySphere(); + delete verifyAllSeparating(tri, "Poincare homology sphere"); + + tri = Example<3>::weeks(); + delete verifyAllSeparating(tri, "Weeks-Matveev-Fomenko manifold"); + + + // Manifolds with nonseparating surfaces + + tri = Example<3>::s2xs1(); + delete verifyHasNonSeparating(tri, "S2xS1"); + + tri = Example<3>::rp2xs1(); + delete verifyHasNonSeparating(tri, "RP2xS1"); + + tri = Example<3>::rp3rp3(); + delete verifyHasNonSeparating(tri, "RP3#RP3"); + + tri = Example<3>::smallClosedNonOrblHyperbolic(); + delete verifyHasNonSeparating(tri, "Smallest known closed nonorientable hyperbolic"); + + p = 3; + q = 2; + while (p < 1000){ + tri = Example<3>::lst(p,q); + delete verifyHasNonSeparating(tri, "Solid torus"); + if (p % 2 == 0){ + tri = Example<3>::lens(p,q); + delete verifyHasNonSeparating(tri, "Lens space with even p"); + } + p = p + q; + q = p - q; + } + + tri = Example<3>::solidKleinBottle(); + delete verifyHasNonSeparating(tri, "Solid Klein bottle"); + + tri = Example<3>::figureEight(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyHasNonSeparating(tri, "Figure eight"); + + tri = Example<3>::trefoil(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyHasNonSeparating(tri, "Trefoil"); + + tri = Example<3>::whiteheadLink(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyHasNonSeparating(tri, "Whitehead link"); + + tri = Example<3>::gieseking(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyHasNonSeparating(tri, "Gieseking manifold"); + + tri = Example<3>::cuspedGenusTwoTorus(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyHasNonSeparating(tri, "Genus two surface x I"); + } + + void isEssentialSphere() { + CPPUNIT_FAIL("Not implemented yet"); + } + + void isEssentialTorus() { + CPPUNIT_FAIL("Not implemented yet"); + } + + void isSolidTorusAnnulus() { + CPPUNIT_FAIL("Not implemented yet"); + } + +}; + +void addFaultFinding(CppUnit::TextUi::TestRunner& runner) { + runner.addTest(FaultTest::suite()); +} diff --git a/testsuite/surfaces/testsurfaces.h b/testsuite/surfaces/testsurfaces.h index 01d8d121a3..4c4ed25124 100644 --- a/testsuite/surfaces/testsurfaces.h +++ b/testsuite/surfaces/testsurfaces.h @@ -44,4 +44,5 @@ void addNormalSurfaces(CppUnit::TextUi::TestRunner& runner); void addIncompressible(CppUnit::TextUi::TestRunner& runner); +void addFaultFinding(CppUnit::TextUi::TestRunner& runner); diff --git a/testsuite/testsuite.cpp b/testsuite/testsuite.cpp index 735e38b362..92d97ab277 100644 --- a/testsuite/testsuite.cpp +++ b/testsuite/testsuite.cpp @@ -159,6 +159,7 @@ bool populateTests(CppUnit::TextTestRunner& runner, int argc, char* argv[]) { // Surfaces: sets.insert(std::make_pair("normalsurfaces", &addNormalSurfaces)); sets.insert(std::make_pair("incompressible", &addIncompressible)); + sets.insert(std::make_pair("faultfinding", &addFaultFinding)); // Angle structures: sets.insert(std::make_pair("anglestructures", &addAngleStructures)); From 62740d4787542081115e8a4ccf1f6d18bba5e10b Mon Sep 17 00:00:00 2001 From: "Robert C. Haraway, III" Date: Wed, 28 Oct 2020 10:06:38 -0500 Subject: [PATCH 03/31] Fixed separation testing Fixed the tests for separating surfaces in lens spaces. --- engine/surfaces/fault.cpp | 4 +- testsuite/surfaces/fault.cpp | 130 +++++++++++++++++++++++++++-------- 2 files changed, 105 insertions(+), 29 deletions(-) diff --git a/engine/surfaces/fault.cpp b/engine/surfaces/fault.cpp index 684e487910..89c2047cf3 100644 --- a/engine/surfaces/fault.cpp +++ b/engine/surfaces/fault.cpp @@ -36,8 +36,8 @@ namespace regina { bool NormalSurface::separates() const{ - int tri_cpts = this->triangulation()->countComponents(); - Triangulation<3>* cut_up = this->cutAlong(); + int tri_cpts = triangulation()->countComponents(); + Triangulation<3>* cut_up = cutAlong(); cut_up->intelligentSimplify(); int new_cpts = cut_up->countComponents(); delete cut_up; diff --git a/testsuite/surfaces/fault.cpp b/testsuite/surfaces/fault.cpp index 2c1c80cdfe..fdb181f190 100644 --- a/testsuite/surfaces/fault.cpp +++ b/testsuite/surfaces/fault.cpp @@ -31,6 +31,9 @@ **************************************************************************/ #include +#include +#include "surfaces/normalcoords.h" +#include "surfaces/normalflags.h" #include "surfaces/normalsurface.h" #include "surfaces/normalsurfaces.h" #include "triangulation/dim3.h" @@ -39,9 +42,18 @@ using regina::Example; +using regina::NormalListFlags; using regina::NormalSurfaces; +using regina::NS_EMBEDDED_ONLY; +using regina::NS_FUNDAMENTAL; +using regina::NS_QUAD; +using regina::NS_VERTEX; using regina::Triangulation; +using std::ostringstream; + +NormalListFlags NS_VERTEX_EMBEDDED = (NormalListFlags)(NS_VERTEX & NS_EMBEDDED_ONLY); + class FaultTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(FaultTest); @@ -59,10 +71,46 @@ class FaultTest : public CppUnit::TestFixture { void tearDown() { } - Triangulation<3>* verifyAllSeparating(Triangulation<3>* tri, + + Triangulation<3>* verifyAllVertexSeparating(Triangulation<3>* tri, + const std::string& triName) { + NormalSurfaces* s = NormalSurfaces::enumerate( + tri, NS_QUAD, NS_VERTEX_EMBEDDED); + bool nonseparating_known = false; + for (unsigned i = 0; i < s->size(); ++i) + if (!(s->surface(i)->separates())){ + nonseparating_known = true; + break; + } + delete s; + if (nonseparating_known) + CPPUNIT_FAIL(("A surface in " + triName + + " is computed to be nonseparating.").c_str()); + return tri; + } + + Triangulation<3>* verifyHasVertexNonSeparating(Triangulation<3>* tri, + const std::string& triName){ + NormalSurfaces* s = NormalSurfaces::enumerate( + tri, NS_QUAD, NS_VERTEX_EMBEDDED); + bool all_known_surfaces_separating = true; + for (unsigned i = 0; i < s->size(); ++i) + if (!(s->surface(i)->separates())){ + all_known_surfaces_separating = false; + break; + } + delete s; + if (all_known_surfaces_separating) + CPPUNIT_FAIL(("No surfaces in " + triName + + " were computed to be nonseparating.").c_str()); + return tri; + } + + /* + Triangulation<3>* verifyAllFundamentalSeparating(Triangulation<3>* tri, const std::string& triName) { NormalSurfaces* s = NormalSurfaces::enumerate( - tri, regina::NS_STANDARD, regina::NS_EMBEDDED_ONLY); + tri, NS_QUAD, NS_VERTEX_EMBEDDED); bool nonseparating_known = false; for (unsigned i = 0; i < s->size(); ++i) if (!(s->surface(i)->separates())){ @@ -75,11 +123,12 @@ class FaultTest : public CppUnit::TestFixture { " is computed to be nonseparating.").c_str()); return tri; } + */ - Triangulation<3>* verifyHasNonSeparating(Triangulation<3>* tri, + Triangulation<3>* verifyHasFundamentalNonSeparating(Triangulation<3>* tri, const std::string& triName){ NormalSurfaces* s = NormalSurfaces::enumerate( - tri, regina::NS_STANDARD, regina::NS_EMBEDDED_ONLY); + tri, NS_QUAD, NS_FUNDAMENTAL); bool all_known_surfaces_separating = true; for (unsigned i = 0; i < s->size(); ++i) if (!(s->surface(i)->separates())){ @@ -93,96 +142,123 @@ class FaultTest : public CppUnit::TestFixture { return tri; } + /** + * All we can conclude from S not being a vertex surface + * is that there is some equation of the form + * + * nS + T = X + Y + * + * for some natural n > 0, trivial surface T, and normal surfaces X, Y. + * Even if there is a nonseparating surface, + * we cannot conclude that some vertex of its support is also nonseparating, + * because the above n might be even, in which case nS is separating. + * So if there is a nonseparating surface in a triangulation, + * nevertheless there might not be a nonseparating vertex-normal such surface. + * + * For example, L(34,13) has H_2(L(34,13),Z_2) = Z_2. + * So L(34,13) admits a nonseparating surface. + * But fLAMcbcbdeehxwqhr = L(34,13) has no nonseparating quad-vertex surface. + * + * However, there must be a nonseparating fundamental normal such surface. + * For instance, in fLAMcbcbdeehxwqhr there is a fundamental P2#P2#P2. + * + * Thus we should have backup tests for manifolds + * with no Z second homology but with Z_2 second homology. + */ + void separates() { Triangulation<3>* tri; // Manifolds without nonseparating surfaces tri = Example<3>::threeSphere(); - delete verifyAllSeparating(tri, "Minimal 3-sphere"); + delete verifyAllVertexSeparating(tri, "Minimal 3-sphere"); tri = Example<3>::simplicialSphere(); - delete verifyAllSeparating(tri, "Pentachoron boundary 3-sphere"); + delete verifyAllVertexSeparating(tri, "Pentachoron boundary 3-sphere"); tri = Example<3>::ball(); - delete verifyAllSeparating(tri, "One-tetrahedron ball"); + delete verifyAllVertexSeparating(tri, "One-tetrahedron ball"); int p = 3; int q = 2; - while (p < 1000){ + while (p <= 34){ if (p % 2 != 0){ tri = Example<3>::lens(p,q); - delete verifyAllSeparating(tri, "Lens space with odd p"); + ostringstream oss; + oss << "L(" << p << "," << q << ")" ; + delete verifyAllVertexSeparating(tri, oss.str()); } p = p + q; q = p - q; } tri = Example<3>::poincareHomologySphere(); - delete verifyAllSeparating(tri, "Poincare homology sphere"); + delete verifyAllVertexSeparating(tri, "Poincare homology sphere"); tri = Example<3>::weeks(); - delete verifyAllSeparating(tri, "Weeks-Matveev-Fomenko manifold"); - + delete verifyAllVertexSeparating(tri, "Weeks-Matveev-Fomenko manifold"); // Manifolds with nonseparating surfaces tri = Example<3>::s2xs1(); - delete verifyHasNonSeparating(tri, "S2xS1"); + delete verifyHasVertexNonSeparating(tri, "S2xS1"); tri = Example<3>::rp2xs1(); - delete verifyHasNonSeparating(tri, "RP2xS1"); + delete verifyHasVertexNonSeparating(tri, "RP2xS1"); tri = Example<3>::rp3rp3(); - delete verifyHasNonSeparating(tri, "RP3#RP3"); + delete verifyHasVertexNonSeparating(tri, "RP3#RP3"); tri = Example<3>::smallClosedNonOrblHyperbolic(); - delete verifyHasNonSeparating(tri, "Smallest known closed nonorientable hyperbolic"); + delete verifyHasVertexNonSeparating(tri, "Smallest known closed nonorientable hyperbolic"); p = 3; q = 2; - while (p < 1000){ + while (p <= 34){ tri = Example<3>::lst(p,q); - delete verifyHasNonSeparating(tri, "Solid torus"); + delete verifyHasVertexNonSeparating(tri, "Solid torus"); if (p % 2 == 0){ tri = Example<3>::lens(p,q); - delete verifyHasNonSeparating(tri, "Lens space with even p"); + ostringstream oss; + oss << "L(" << p << "," << q << ")" ; + delete verifyHasFundamentalNonSeparating(tri, oss.str()); } p = p + q; q = p - q; } tri = Example<3>::solidKleinBottle(); - delete verifyHasNonSeparating(tri, "Solid Klein bottle"); + delete verifyHasVertexNonSeparating(tri, "Solid Klein bottle"); tri = Example<3>::figureEight(); tri->idealToFinite(); tri->intelligentSimplify(); - delete verifyHasNonSeparating(tri, "Figure eight"); + delete verifyHasVertexNonSeparating(tri, "Figure eight"); tri = Example<3>::trefoil(); tri->idealToFinite(); tri->intelligentSimplify(); - delete verifyHasNonSeparating(tri, "Trefoil"); + delete verifyHasVertexNonSeparating(tri, "Trefoil"); tri = Example<3>::whiteheadLink(); tri->idealToFinite(); tri->intelligentSimplify(); - delete verifyHasNonSeparating(tri, "Whitehead link"); + delete verifyHasVertexNonSeparating(tri, "Whitehead link"); tri = Example<3>::gieseking(); tri->idealToFinite(); tri->intelligentSimplify(); - delete verifyHasNonSeparating(tri, "Gieseking manifold"); + delete verifyHasVertexNonSeparating(tri, "Gieseking manifold"); tri = Example<3>::cuspedGenusTwoTorus(); tri->idealToFinite(); tri->intelligentSimplify(); - delete verifyHasNonSeparating(tri, "Genus two surface x I"); + delete verifyHasVertexNonSeparating(tri, "Genus two surface x I"); } - + void isEssentialSphere() { - CPPUNIT_FAIL("Not implemented yet"); + CPPUNIT_FAIL("Not implemented yet"); } void isEssentialTorus() { From 34b3a3a83044965b5e74bda56508e0d6b0be9183 Mon Sep 17 00:00:00 2001 From: "Robert C. Haraway, III" Date: Wed, 28 Oct 2020 10:34:10 -0500 Subject: [PATCH 04/31] Finished tests for essential spheres Finished implementing tests for `isEssentialSphere` member function of `NormalSurface` class. --- testsuite/surfaces/fault.cpp | 120 ++++++++++++++++++++++++++++++++++- 1 file changed, 119 insertions(+), 1 deletion(-) diff --git a/testsuite/surfaces/fault.cpp b/testsuite/surfaces/fault.cpp index fdb181f190..f70fe9e948 100644 --- a/testsuite/surfaces/fault.cpp +++ b/testsuite/surfaces/fault.cpp @@ -256,9 +256,127 @@ class FaultTest : public CppUnit::TestFixture { tri->intelligentSimplify(); delete verifyHasVertexNonSeparating(tri, "Genus two surface x I"); } + + + Triangulation<3>* verifyAllVertexSpheresTrivial(Triangulation<3>* tri, + const std::string& triName){ + NormalSurfaces* s = NormalSurfaces::enumerate( + tri, NS_QUAD, NS_VERTEX_EMBEDDED); + bool found_essential_sphere = false; + for (unsigned i = 0; i < s->size(); ++i) + if (s->surface(i)->isEssentialSphere()){ + found_essential_sphere = true; + break; + } + delete s; + if (found_essential_sphere) + CPPUNIT_FAIL(("The irreducible manifold " + triName + + " was computed to have an essential sphere.").c_str()); + return tri; + } + + Triangulation<3>* verifyHasVertexEssentialSphere(Triangulation<3>* tri, + const std::string& triName){ + NormalSurfaces* s = NormalSurfaces::enumerate( + tri, NS_QUAD, NS_VERTEX_EMBEDDED); + bool found_essential_sphere = false; + for (unsigned i = 0; i < s->size(); ++i) + if (s->surface(i)->isEssentialSphere()){ + found_essential_sphere = true; + break; + } + delete s; + if (!found_essential_sphere) + CPPUNIT_FAIL(("No vertex essential spheres were found in " + + triName + ".").c_str()); + return tri; + } + void isEssentialSphere() { - CPPUNIT_FAIL("Not implemented yet"); + Triangulation<3>* tri; + + // Closed irreducible manifolds + tri = Example<3>::threeSphere(); + delete verifyAllVertexSpheresTrivial(tri, "Minimal 3-sphere"); + tri = Example<3>::simplicialSphere(); + delete verifyAllVertexSpheresTrivial(tri, "Pentachoron boundary 3-sphere"); + tri = Example<3>::poincareHomologySphere(); + delete verifyAllVertexSpheresTrivial(tri, "Poincare homology sphere"); + tri = Example<3>::weeks(); + delete verifyAllVertexSpheresTrivial(tri, "Weeks-Matveev-Fomenko manifold"); + int p = 3; + int q = 2; + while (p < 1000){ + tri = Example<3>::lens(p,q); + ostringstream oss; + oss << "L(" << p << "," << q << ")"; + delete verifyAllVertexSpheresTrivial(tri, oss.str()); + p = p + q; + q = p - q; + } + + // Bounded irreducible manifolds + tri = Example<3>::ball(); + delete verifyAllVertexSpheresTrivial(tri, "One tetrahedron ball"); + tri = Example<3>::cuspedGenusTwoTorus(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyAllVertexSpheresTrivial( + tri, "Trivial I-bundle over genus two surface"); + tri = Example<3>::figureEight(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyAllVertexSpheresTrivial(tri, "Figure eight knot exterior"); + tri = Example<3>::trefoil(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyAllVertexSpheresTrivial(tri, "Trefoil knot exterior"); + tri = Example<3>::whiteheadLink(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyAllVertexSpheresTrivial(tri, "Whitehead link exterior"); + tri = Example<3>::gieseking(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyAllVertexSpheresTrivial(tri, "Gieseking manifold"); + tri = Example<3>::solidKleinBottle(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyAllVertexSpheresTrivial(tri, "Solid Klein bottle"); + p = 3; + q = 2; + while (p < 1000){ + tri = Example<3>::lst(p,q); + delete verifyAllVertexSpheresTrivial(tri, "Solid torus"); + p = p + q; + q = p - q; + } + + // The prime reducible manifold S^2 x S^1 + tri = Example<3>::s2xs1(); + delete verifyHasVertexEssentialSphere(tri, "S2xS1"); + + // RP3#RP3 has a reducing sphere, + // but in the given triangulation, there is no fundamental reducing sphere. + // Instead, there are two fundamental embedded one-sided RP2s + // that double to reducing spheres. + + tri = Example<3>::rp3rp3(); + delete verifyAllVertexSpheresTrivial(tri, "RP3#RP3"); + + p = 3; + q = 2; + while (p < 100){ + tri = Example<3>::lens(p,q); + tri->connectedSumWith(*tri); + tri->intelligentSimplify(); + ostringstream oss; + oss << "L(" << p << "," << q << ")"; + delete verifyHasVertexEssentialSphere(tri, oss.str() + " # " + oss.str()); + p = p + q; + q = p - q; + } } void isEssentialTorus() { From 43db969f357c0e7ce109b0c4b51ac8e365d9a61e Mon Sep 17 00:00:00 2001 From: "Robert C. Haraway, III" Date: Wed, 28 Oct 2020 10:45:19 -0500 Subject: [PATCH 05/31] Added Python bindings for fault tests Added Python bindings for the member functions `separates`, `isEssentialSphere`, `isEssentialTorus`, `isEssentialKleinBottle`, and `isSolidTorusAnnulus` of the `NormalSurface` class. --- python/surfaces/normalsurface.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/python/surfaces/normalsurface.cpp b/python/surfaces/normalsurface.cpp index 4cb428ad4b..929fdc54ec 100644 --- a/python/surfaces/normalsurface.cpp +++ b/python/surfaces/normalsurface.cpp @@ -117,6 +117,11 @@ void addNormalSurface(pybind11::module& m) { .def("countBoundaries", &NormalSurface::countBoundaries) .def("isCompressingDisc", &NormalSurface::isCompressingDisc, pybind11::arg("knownConnected") = false) + .def("separates", &NormalSurface::separates) + .def("isEssentialSphere", &NormalSurface::isEssentialSphere) + .def("isEssentialTorus", &NormalSurface::isEssentialTorus) + .def("isEssentialKleinBottle", &NormalSurface::isEssentialKleinBottle) + .def("isSolidTorusAnnulus", &NormalSurface::isSolidTorusAnnulus) .def("isIncompressible", &NormalSurface::isIncompressible) .def("cutAlong", &NormalSurface::cutAlong) .def("crush", &NormalSurface::crush) From 8a0145dbcf1a3f69e8a71438bb5784cdf1dae60d Mon Sep 17 00:00:00 2001 From: "Robert C. Haraway, III" Date: Wed, 28 Oct 2020 10:46:45 -0500 Subject: [PATCH 06/31] Finished scaffolding for fault tests Added header info for a missing function in `NormalSurface.h`. --- engine/surfaces/normalsurface.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/engine/surfaces/normalsurface.h b/engine/surfaces/normalsurface.h index 155e053540..77d9f14a3e 100644 --- a/engine/surfaces/normalsurface.h +++ b/engine/surfaces/normalsurface.h @@ -1451,6 +1451,8 @@ class REGINA_API NormalSurface : public ShortOutput { bool isEssentialSphere() const; + bool isEssentialKleinBottle() const; + bool isEssentialTorus() const; /** From b705845b87f0f5fc7c4a2890c82b328a10432432 Mon Sep 17 00:00:00 2001 From: "Robert C. Haraway, III" Date: Wed, 28 Oct 2020 11:09:49 -0500 Subject: [PATCH 07/31] Essential sphere test Added unit tests and methods for `isEssentialSphere` member function of `NormalSurface` class. --- engine/surfaces/fault.cpp | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/engine/surfaces/fault.cpp b/engine/surfaces/fault.cpp index 89c2047cf3..e95c5a2577 100644 --- a/engine/surfaces/fault.cpp +++ b/engine/surfaces/fault.cpp @@ -45,6 +45,37 @@ namespace regina { } bool NormalSurface::isEssentialSphere() const{ + if (!(isConnected() + && isCompact() + && eulerChar() == 2)) + return false; + + int tri_cpts = triangulation()->countComponents(); + Triangulation<3>* cut_up = cutAlong(); + cut_up->intelligentSimplify(); + int new_cpts = cut_up->countComponents(); + if (tri_cpts >= new_cpts){ + delete cut_up; + return true; + } + + // Cap sphere boundary components. + cut_up->finiteToIdeal(); + cut_up->intelligentSimplify(); + cut_up->idealToFinite(); + cut_up->intelligentSimplify(); + + cut_up->splitIntoComponents(); + Triangulation<3>* L = (Triangulation<3>*) cut_up->firstChild(); + Triangulation<3>* R = (Triangulation<3>*) L->nextSibling(); + bool essential = !(L->isThreeSphere() || R->isThreeSphere()); + delete L; + delete R; + delete cut_up; + return essential; + } + + bool NormalSurface::isEssentialKleinBottle() const{ return false; } From 4c9b7296ad5a34ff7ce19f83353d1b2d96190f26 Mon Sep 17 00:00:00 2001 From: "Robert C. Haraway, III" Date: Wed, 28 Oct 2020 12:00:47 -0500 Subject: [PATCH 08/31] Implement tests for essential Klein bottle test Implemented tests for `isEssentialKleinBottle` member function of `NormalSurface` class. --- testsuite/surfaces/fault.cpp | 155 +++++++++++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) diff --git a/testsuite/surfaces/fault.cpp b/testsuite/surfaces/fault.cpp index f70fe9e948..039af05f6e 100644 --- a/testsuite/surfaces/fault.cpp +++ b/testsuite/surfaces/fault.cpp @@ -32,6 +32,7 @@ #include #include +#include "manifold/sfs.h" #include "surfaces/normalcoords.h" #include "surfaces/normalflags.h" #include "surfaces/normalsurface.h" @@ -48,6 +49,8 @@ using regina::NS_EMBEDDED_ONLY; using regina::NS_FUNDAMENTAL; using regina::NS_QUAD; using regina::NS_VERTEX; +using regina::SFSFibre; +using regina::SFSpace; using regina::Triangulation; using std::ostringstream; @@ -59,6 +62,7 @@ class FaultTest : public CppUnit::TestFixture { CPPUNIT_TEST(separates); CPPUNIT_TEST(isEssentialSphere); + CPPUNIT_TEST(isEssentialKleinBottle); CPPUNIT_TEST(isEssentialTorus); CPPUNIT_TEST(isSolidTorusAnnulus); @@ -378,6 +382,157 @@ class FaultTest : public CppUnit::TestFixture { q = p - q; } } + + Triangulation<3>* verifyAllVertexKleinBottlesInessential(Triangulation<3>* tri, + const std::string& triName){ + NormalSurfaces* s = NormalSurfaces::enumerate( + tri, NS_QUAD, NS_VERTEX_EMBEDDED); + bool found_essential_klein_bottle = false; + for (unsigned i = 0; i < s->size(); ++i) + if (s->surface(i)->isEssentialKleinBottle()){ + found_essential_klein_bottle = true; + break; + } + delete s; + if (found_essential_klein_bottle) + CPPUNIT_FAIL((triName + + " has an \"essential Klein bottle.\"").c_str()); + return tri; + } + + Triangulation<3>* verifyHasVertexEssentialKleinBottle(Triangulation<3>* tri, + const std::string& triName){ + NormalSurfaces* s = NormalSurfaces::enumerate( + tri, NS_QUAD, NS_VERTEX_EMBEDDED); + bool found_essential_klein_bottle = false; + for (unsigned i = 0; i < s->size(); ++i) + if (s->surface(i)->isEssentialKleinBottle()){ + found_essential_klein_bottle = true; + break; + } + delete s; + if (!found_essential_klein_bottle) + CPPUNIT_FAIL(("No vertex essential Klein bottle found in " + + triName + ".").c_str()); + return tri; + } + + + Triangulation<3>* smallSFS(long alpha0, long beta0, + long alpha1, long beta1, + long alpha2, long beta2){ + const SFSFibre* fiber0 = new SFSFibre(alpha0, beta0); + const SFSFibre* fiber1 = new SFSFibre(alpha1, beta1); + const SFSFibre* fiber2 = new SFSFibre(alpha2, beta2); + SFSpace* sfs = new SFSpace( + SFSpace::o1, 0, 0, 0, 0, 0); + sfs->insertFibre(*fiber0); + sfs->insertFibre(*fiber1); + sfs->insertFibre(*fiber2); + Triangulation<3>* sfs_tri = sfs->construct(); + delete sfs; + delete fiber0; + delete fiber1; + delete fiber2; + return sfs_tri; + } + + void isEssentialKleinBottle() { + Triangulation<3>* tri; + + // Manifolds with no essential Klein bottles + + tri = Example<3>::threeSphere(); + delete verifyAllVertexKleinBottlesInessential(tri, "Minimal 3-sphere"); + + tri = Example<3>::simplicialSphere(); + delete verifyAllVertexKleinBottlesInessential(tri, "Pentachoron boundary 3-sphere"); + + tri = Example<3>::ball(); + delete verifyAllVertexKleinBottlesInessential(tri, "One-tetrahedron ball"); + + int p = 3; + int q = 2; + while (p <= 34){ + if (p % 2 != 0){ + tri = Example<3>::lens(p,q); + ostringstream oss; + oss << "L(" << p << "," << q << ")" ; + delete verifyAllVertexKleinBottlesInessential(tri, oss.str()); + } + p = p + q; + q = p - q; + } + + tri = Example<3>::poincareHomologySphere(); + delete verifyAllVertexKleinBottlesInessential(tri, "Poincare homology sphere"); + + tri = Example<3>::weeks(); + delete verifyAllVertexKleinBottlesInessential(tri, "Weeks-Matveev-Fomenko manifold"); + + tri = Example<3>::s2xs1(); + delete verifyAllVertexKleinBottlesInessential(tri, "S2xS1"); + + tri = Example<3>::rp2xs1(); + delete verifyAllVertexKleinBottlesInessential(tri, "RP2xS1"); + + tri = Example<3>::rp3rp3(); + delete verifyAllVertexKleinBottlesInessential(tri, "RP3#RP3"); + + tri = Example<3>::smallClosedNonOrblHyperbolic(); + delete verifyAllVertexKleinBottlesInessential(tri, "Smallest known closed nonorientable hyperbolic"); + + p = 3; + q = 2; + while (p <= 34){ + tri = Example<3>::lst(p,q); + delete verifyAllVertexKleinBottlesInessential(tri, "Solid torus"); + if (p % 2 == 0){ + tri = Example<3>::lens(p,q); + ostringstream oss; + oss << "L(" << p << "," << q << ")" ; + delete verifyAllVertexKleinBottlesInessential(tri, oss.str()); + } + p = p + q; + q = p - q; + } + + tri = Example<3>::solidKleinBottle(); + delete verifyAllVertexKleinBottlesInessential(tri, "Solid Klein bottle"); + + tri = Example<3>::figureEight(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyAllVertexKleinBottlesInessential(tri, "Figure eight"); + + tri = Example<3>::trefoil(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyAllVertexKleinBottlesInessential(tri, "Trefoil"); + + tri = Example<3>::whiteheadLink(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyAllVertexKleinBottlesInessential(tri, "Whitehead link"); + + tri = Example<3>::gieseking(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyAllVertexKleinBottlesInessential(tri, "Gieseking manifold"); + + tri = Example<3>::cuspedGenusTwoTorus(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyAllVertexKleinBottlesInessential(tri, "Genus two surface x I"); + + // Manifolds with essential Klein bottles + + tri = new Triangulation<3>("gLALQbcceffflpkktua"); + delete verifyHasVertexEssentialKleinBottle(tri, "Doubled twisted I-bundle over K2"); + + tri = new Triangulation<3>("oLLLAAzLMQccefffggjlmnmmnnkkwawpwwjraakru"); + delete verifyHasVertexEssentialKleinBottle(tri, "Doubled Gieseking manifold"); + } void isEssentialTorus() { CPPUNIT_FAIL("Not implemented yet"); From 25f6eedf5bcb0b93613f7148af1b37ff25955cf2 Mon Sep 17 00:00:00 2001 From: "Robert C. Haraway, III" Date: Wed, 28 Oct 2020 12:18:44 -0500 Subject: [PATCH 09/31] Add essential Klein bottle method Added `isEssentialKleinBottle` function to `NormalSurface` class. This depends upon the `isTorusXInterval` function in the `Triangulation<3>` class. --- engine/surfaces/fault.cpp | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/engine/surfaces/fault.cpp b/engine/surfaces/fault.cpp index e95c5a2577..8663046fb1 100644 --- a/engine/surfaces/fault.cpp +++ b/engine/surfaces/fault.cpp @@ -49,15 +49,12 @@ namespace regina { && isCompact() && eulerChar() == 2)) return false; - - int tri_cpts = triangulation()->countComponents(); + + if (!separates()) + return true; + Triangulation<3>* cut_up = cutAlong(); cut_up->intelligentSimplify(); - int new_cpts = cut_up->countComponents(); - if (tri_cpts >= new_cpts){ - delete cut_up; - return true; - } // Cap sphere boundary components. cut_up->finiteToIdeal(); @@ -76,7 +73,30 @@ namespace regina { } bool NormalSurface::isEssentialKleinBottle() const{ - return false; + if (!(isConnected() + && isCompact() + && !isOrientable() + && !hasRealBoundary() + && eulerChar() == 0)) + return false; + + if (!isIncompressible()) + return false; + + if (!separates()) + return true; + + Triangulation<3>* cut_up = cutAlong(); + cut_up->splitIntoComponents(); + Triangulation<3>* L = (Triangulation<3>*) cut_up->firstChild(); + Triangulation<3>* R = (Triangulation<3>*) L->nextSibling(); + L->makeDoubleCover(); + R->makeDoubleCover(); + bool boundary_parallel = L->isTorusXInterval() || R->isTorusXInterval(); + delete L; + delete R; + delete cut_up; + return !boundary_parallel; } bool NormalSurface::isEssentialTorus() const{ From 85b50025196f246a59e611d65727cc9f91f99c8f Mon Sep 17 00:00:00 2001 From: "Robert C. Haraway, III" Date: Wed, 28 Oct 2020 12:45:46 -0500 Subject: [PATCH 10/31] Add small SF spaces to Klein bottle test Add small Seifert fibered spaces to tests for `isEssentialKleinBottle`. --- testsuite/surfaces/fault.cpp | 34 ++++++++++++---------------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/testsuite/surfaces/fault.cpp b/testsuite/surfaces/fault.cpp index 039af05f6e..543b830086 100644 --- a/testsuite/surfaces/fault.cpp +++ b/testsuite/surfaces/fault.cpp @@ -454,12 +454,17 @@ class FaultTest : public CppUnit::TestFixture { int p = 3; int q = 2; while (p <= 34){ - if (p % 2 != 0){ - tri = Example<3>::lens(p,q); - ostringstream oss; - oss << "L(" << p << "," << q << ")" ; - delete verifyAllVertexKleinBottlesInessential(tri, oss.str()); - } + tri = Example<3>::lst(p,q); + delete verifyAllVertexKleinBottlesInessential(tri, "Solid torus"); + + tri = Example<3>::lens(p,q); + ostringstream oss; + oss << "L(" << p << "," << q << ")" ; + delete verifyAllVertexKleinBottlesInessential(tri, oss.str()); + + tri = smallSFS(2,1,2,1,p,-q); + delete verifyAllVertexKleinBottlesInessential(tri, oss.str() ); + p = p + q; q = p - q; } @@ -481,21 +486,6 @@ class FaultTest : public CppUnit::TestFixture { tri = Example<3>::smallClosedNonOrblHyperbolic(); delete verifyAllVertexKleinBottlesInessential(tri, "Smallest known closed nonorientable hyperbolic"); - - p = 3; - q = 2; - while (p <= 34){ - tri = Example<3>::lst(p,q); - delete verifyAllVertexKleinBottlesInessential(tri, "Solid torus"); - if (p % 2 == 0){ - tri = Example<3>::lens(p,q); - ostringstream oss; - oss << "L(" << p << "," << q << ")" ; - delete verifyAllVertexKleinBottlesInessential(tri, oss.str()); - } - p = p + q; - q = p - q; - } tri = Example<3>::solidKleinBottle(); delete verifyAllVertexKleinBottlesInessential(tri, "Solid Klein bottle"); @@ -533,7 +523,7 @@ class FaultTest : public CppUnit::TestFixture { tri = new Triangulation<3>("oLLLAAzLMQccefffggjlmnmmnnkkwawpwwjraakru"); delete verifyHasVertexEssentialKleinBottle(tri, "Doubled Gieseking manifold"); } - + void isEssentialTorus() { CPPUNIT_FAIL("Not implemented yet"); } From 9bc7cdcb826bbb6e1bda7d7018d269f44787ac93 Mon Sep 17 00:00:00 2001 From: "Robert C. Haraway, III" Date: Wed, 28 Oct 2020 13:04:35 -0500 Subject: [PATCH 11/31] Add essential torus test Add `isEssentialTorus` member function to `NormalSurface` class. Add unit tests for `isEssentialTorus`. --- engine/surfaces/fault.cpp | 23 ++++- testsuite/surfaces/fault.cpp | 174 ++++++++++++++++++++++++++++++++++- 2 files changed, 195 insertions(+), 2 deletions(-) diff --git a/engine/surfaces/fault.cpp b/engine/surfaces/fault.cpp index 8663046fb1..c83aff92f7 100644 --- a/engine/surfaces/fault.cpp +++ b/engine/surfaces/fault.cpp @@ -100,7 +100,28 @@ namespace regina { } bool NormalSurface::isEssentialTorus() const{ - return false; + if (!(isConnected() + && isCompact() + && isOrientable() + && !hasRealBoundary() + && eulerChar() == 0)) + return false; + + if (!isIncompressible()) + return false; + + if (!separates()) + return true; + + Triangulation<3>* cut_up = cutAlong(); + cut_up->splitIntoComponents(); + Triangulation<3>* L = (Triangulation<3>*) cut_up->firstChild(); + Triangulation<3>* R = (Triangulation<3>*) L->nextSibling(); + bool boundary_parallel = L->isTorusXInterval() || R->isTorusXInterval(); + delete L; + delete R; + delete cut_up; + return !boundary_parallel; } bool NormalSurface::isSolidTorusAnnulus() const{ diff --git a/testsuite/surfaces/fault.cpp b/testsuite/surfaces/fault.cpp index 543b830086..a46e70867b 100644 --- a/testsuite/surfaces/fault.cpp +++ b/testsuite/surfaces/fault.cpp @@ -524,8 +524,180 @@ class FaultTest : public CppUnit::TestFixture { delete verifyHasVertexEssentialKleinBottle(tri, "Doubled Gieseking manifold"); } + Triangulation<3>* verifyAllVertexToriInessential(Triangulation<3>* tri, + const std::string& triName){ + NormalSurfaces* s = NormalSurfaces::enumerate( + tri, NS_QUAD, NS_VERTEX_EMBEDDED); + bool found_essential_torus = false; + for (unsigned i = 0; i < s->size(); ++i) + if (s->surface(i)->isEssentialTorus()){ + found_essential_torus = true; + break; + } + delete s; + if (found_essential_torus) + CPPUNIT_FAIL(("The atoroidal manifold " + triName + + " was computed to have an essential torus.").c_str()); + return tri; + } + + Triangulation<3>* verifyHasVertexEssentialTorus(Triangulation<3>* tri, + const std::string& triName){ + NormalSurfaces* s = NormalSurfaces::enumerate( + tri, NS_QUAD, NS_VERTEX_EMBEDDED); + bool found_essential_torus = false; + for (unsigned i = 0; i < s->size(); ++i) + if (s->surface(i)->isEssentialTorus()){ + found_essential_torus = true; + break; + } + delete s; + if (!found_essential_torus) + CPPUNIT_FAIL(("No essential tori were found in the toroidal manifold " + + triName + ".").c_str()); + return tri; + } + void isEssentialTorus() { - CPPUNIT_FAIL("Not implemented yet"); + Triangulation<3>* tri; + + // Simple closed atoroidal manifolds + tri = Example<3>::threeSphere(); + delete verifyAllVertexToriInessential(tri, "Minimal 3-sphere"); + tri = Example<3>::simplicialSphere(); + delete verifyAllVertexToriInessential(tri, "Pentachoron boundary 3-sphere"); + tri = Example<3>::poincareHomologySphere(); + delete verifyAllVertexToriInessential(tri, "Poincare homology sphere"); + tri = Example<3>::weeks(); + delete verifyAllVertexToriInessential(tri, "Weeks-Matveev-Fomenko manifold"); + int p = 3; + int q = 2; + while (p < 1000){ + tri = Example<3>::lens(p,q); + delete verifyAllVertexToriInessential(tri, "Fibonacci lens space"); + tri = smallSFS(2,1,2,1,p,-q); + ostringstream oss; + oss << "SFS [S2: (2,1)(2,1)(" << p << "," << -q << ")]"; + delete verifyAllVertexToriInessential(tri, oss.str()); + p = p + q; + q = p - q; + } + + // Small Seifert fibered spaces... +#define VERIFY_SMALL_SFS_ATOROIDAL(a0,b0,a1,b1,a2,b2) \ + tri = smallSFS(a0,b0,a1,b1,a2,b2); \ + tri->intelligentSimplify(); \ + delete verifyAllVertexToriInessential(tri, "SFS [S2: (a0,b0) (a1,b1) (a2,b2)]") + + // ... of positive orbifold Euler characteristic ... + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,1,3,-2); + + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,1,3,-1); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,1,4,-3); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,1,5,-4); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,2,3,-1); + + /* + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,1,3,1); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,1,3,2); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,1,4,-1); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,1,5,-3); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,1,5,-2); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,2,3,2); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,2,4,-3); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,2,4,-1); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,2,5,-3); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,2,5,-2); + */ + + // ...and of negative orbifold Euler characteristic.... + /* + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,1,7,-6); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,1,7,-5); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,1,7,-5); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,1,7,-4); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,1,7,-2); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,1,8,-5); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,1,8,-3); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,2,7,-5); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,2,7,-4); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,2,7,-3); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,2,7,-2); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,2,8,-5); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,2,8,-3); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,2,8,-3); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,4,1,5,-4); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,4,1,5,-3); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,4,1,5,-2); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,4,3,5,-3); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,4,3,5,-2); + */ + VERIFY_SMALL_SFS_ATOROIDAL(2,1,5,2,5,-3); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,5,2,5,-2); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,5,3,5,-2); + + VERIFY_SMALL_SFS_ATOROIDAL(3,1,3,1,4,-3); + VERIFY_SMALL_SFS_ATOROIDAL(3,1,3,1,4,-1); + VERIFY_SMALL_SFS_ATOROIDAL(3,1,3,1,5,-3); + VERIFY_SMALL_SFS_ATOROIDAL(3,1,3,1,5,-2); + /* + VERIFY_SMALL_SFS_ATOROIDAL(3,1,3,2,4,-3); + VERIFY_SMALL_SFS_ATOROIDAL(3,1,3,2,4,-1); + VERIFY_SMALL_SFS_ATOROIDAL(3,1,3,2,5,-3); + VERIFY_SMALL_SFS_ATOROIDAL(3,1,3,2,5,-2); + VERIFY_SMALL_SFS_ATOROIDAL(3,2,3,2,4,-3); + VERIFY_SMALL_SFS_ATOROIDAL(3,2,3,2,4,-1); + VERIFY_SMALL_SFS_ATOROIDAL(3,2,3,2,5,-3); + VERIFY_SMALL_SFS_ATOROIDAL(3,2,3,2,5,-2); + */ + + // Bounded atoroidal manifolds + tri = Example<3>::ball(); + delete verifyAllVertexToriInessential(tri, "One tetrahedron ball"); + tri = Example<3>::cuspedGenusTwoTorus(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyAllVertexToriInessential( + tri, "Trivial I-bundle over genus two surface"); + tri = Example<3>::figureEight(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyAllVertexToriInessential(tri, "Figure eight knot exterior"); + tri = Example<3>::trefoil(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyAllVertexToriInessential(tri, "Trefoil knot exterior"); + tri = Example<3>::whiteheadLink(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyAllVertexToriInessential(tri, "Whitehead link exterior"); + tri = Example<3>::gieseking(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyAllVertexToriInessential(tri, "Gieseking manifold"); + tri = Example<3>::solidKleinBottle(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyAllVertexToriInessential(tri, "Solid Klein bottle"); + p = 3; + q = 2; + while (p < 1000){ + tri = Example<3>::lst(p,q); + delete verifyAllVertexToriInessential(tri, "Solid torus"); + p = p + q; + q = p - q; + } + + // Toroidal manifolds + tri = new Triangulation<3>("uLLvPAPAzzvQPQccdeghiihjjlmqspstrstthshgbvrndhakecbcqvndm"); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyHasVertexEssentialTorus(tri, "Doubled figure eight knot exterior"); + + tri = new Triangulation<3>("iLALzQcbccefhgghlpkkucjjs"); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyHasVertexEssentialTorus(tri, "Doubled trefoil knot exterior"); } void isSolidTorusAnnulus() { From 6a91a8a734947f5ffbd36003d8d4e1b41e784e1e Mon Sep 17 00:00:00 2001 From: "Robert C. Haraway, III" Date: Thu, 29 Oct 2020 10:37:14 -0500 Subject: [PATCH 12/31] Added preconditions for fault tests to documentation `isEssentialSphere` is only correct for connected triangulations with no sphere boundary components. `isEssentialKleinBottle` and `isEssentialTorus` are only correct for connected irreducible boundary-incompressible triangulations. --- engine/surfaces/normalsurface.h | 36 +++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/engine/surfaces/normalsurface.h b/engine/surfaces/normalsurface.h index 77d9f14a3e..58ca0caafd 100644 --- a/engine/surfaces/normalsurface.h +++ b/engine/surfaces/normalsurface.h @@ -1449,16 +1449,48 @@ class REGINA_API NormalSurface : public ShortOutput { */ bool separates() const; + /** + * Determines whether or not this surface is an essential sphere. + * + * \pre The underlying triangulation is connected + * and has no sphere boundary components. + * \pre This normal surface can be cut along. + * + * @return \c true if this is an essential sphere, otherwise \c false. + */ bool isEssentialSphere() const; + /** + * Determines whether or not this surface is an essential Klein bottle. + * + * \pre The underlying triangulation is connected and irreducible, + * and has no compressing disc (i.e. is boundary-incompressible). + * \pre This normal surface can be cut along. + * + * @return \c true if this is an essential Klein bottle, else \c false. + */ bool isEssentialKleinBottle() const; - + + /** + * Determines whether or not this surface is an essential torus. + * + * \pre The underlying triangulation is connected and irreducible, + * and has no compressing disc (i.e. is boundary-incompressible). + * \pre This normal surface can be cut along. + * + * @return \c true if this is an essential torus, else \c false. + */ bool isEssentialTorus() const; /** * Determines whether or not this surface is an annulus - * cutting along which yields two solid tori. + * cutting along which yields a disjoint union of at most two solid tori, + * which we call a \e solid \e torus \e annulus. * This is easier to check than whether or not the annulus is essential. + * + * \pre This normal surface can be cut along. + * + * @return \c true if this surface is a solid torus annulus, else \c false. */ bool isSolidTorusAnnulus() const; From b1883375fe5f088b2e17e664494d64c9cb4cf236 Mon Sep 17 00:00:00 2001 From: "Robert C. Haraway, III" Date: Thu, 29 Oct 2020 14:17:14 -0500 Subject: [PATCH 13/31] Fix nonseparating compressibility error Make `isEssentialKleinBottle` and `isEssentialTorus` return \c false given a nonseparating compressible K2 (resp. T2). --- engine/surfaces/fault.cpp | 72 +++++++++++++++++++++++++++--------- testsuite/surfaces/fault.cpp | 20 +++++++--- 2 files changed, 69 insertions(+), 23 deletions(-) diff --git a/engine/surfaces/fault.cpp b/engine/surfaces/fault.cpp index c83aff92f7..036ab06051 100644 --- a/engine/surfaces/fault.cpp +++ b/engine/surfaces/fault.cpp @@ -52,24 +52,30 @@ namespace regina { if (!separates()) return true; - + Triangulation<3>* cut_up = cutAlong(); cut_up->intelligentSimplify(); // Cap sphere boundary components. + // Since the original triangulation has no sphere boundary components, + // this caps the sides of this sphere. + // That is, this undoes a (possibly trivial) connect-sum. cut_up->finiteToIdeal(); cut_up->intelligentSimplify(); cut_up->idealToFinite(); cut_up->intelligentSimplify(); + // There are two components, + // since the original triangulation was connected, + // and this surface is separating. cut_up->splitIntoComponents(); Triangulation<3>* L = (Triangulation<3>*) cut_up->firstChild(); Triangulation<3>* R = (Triangulation<3>*) L->nextSibling(); - bool essential = !(L->isThreeSphere() || R->isThreeSphere()); + bool bounds_ball = L->isThreeSphere() || R->isThreeSphere(); delete L; delete R; delete cut_up; - return essential; + return !bounds_ball; } bool NormalSurface::isEssentialKleinBottle() const{ @@ -77,26 +83,44 @@ namespace regina { && isCompact() && !isOrientable() && !hasRealBoundary() - && eulerChar() == 0)) + && eulerChar() == 0)){ return false; - + } + + if (!separates()){ + Triangulation<3>* cut_up = cutAlong(); + bool compressible = cut_up->hasCompressingDisc(); + delete cut_up; + return !compressible; + } + + /* if (!isIncompressible()) return false; - - if (!separates()) - return true; - + */ + // is what we would like to write here, + // but presently `isIncompressible` requires closed triangulations. + + // There are two components, + // since the original triangulation was connected, + // and this surface is separating. Triangulation<3>* cut_up = cutAlong(); cut_up->splitIntoComponents(); Triangulation<3>* L = (Triangulation<3>*) cut_up->firstChild(); Triangulation<3>* R = (Triangulation<3>*) L->nextSibling(); - L->makeDoubleCover(); - R->makeDoubleCover(); - bool boundary_parallel = L->isTorusXInterval() || R->isTorusXInterval(); + + bool compressible = true; + bool boundary_parallel = false; + compressible = L->hasCompressingDisc() || R->hasCompressingDisc(); + if (!compressible){ + L->makeDoubleCover(); + R->makeDoubleCover(); + boundary_parallel = L->isTorusXInterval() || R->isTorusXInterval(); + } delete L; delete R; delete cut_up; - return !boundary_parallel; + return !(compressible || boundary_parallel); } bool NormalSurface::isEssentialTorus() const{ @@ -107,21 +131,33 @@ namespace regina { && eulerChar() == 0)) return false; - if (!isIncompressible()) - return false; - if (!separates()) return true; + /* + if (!isIncompressible()) + return false; + */ + // is what we would like to write here, + // but presently `isIncompressible` requires closed triangulations. + // There are two components, + // since the original triangulation was connected, + // and this surface is separating. Triangulation<3>* cut_up = cutAlong(); cut_up->splitIntoComponents(); Triangulation<3>* L = (Triangulation<3>*) cut_up->firstChild(); Triangulation<3>* R = (Triangulation<3>*) L->nextSibling(); - bool boundary_parallel = L->isTorusXInterval() || R->isTorusXInterval(); + + bool compressible = true; + bool boundary_parallel = false; + compressible = L->hasCompressingDisc() || R->hasCompressingDisc(); + if (!compressible){ + boundary_parallel = L->isTorusXInterval() || R->isTorusXInterval(); + } delete L; delete R; delete cut_up; - return !boundary_parallel; + return !(compressible || boundary_parallel); } bool NormalSurface::isSolidTorusAnnulus() const{ diff --git a/testsuite/surfaces/fault.cpp b/testsuite/surfaces/fault.cpp index a46e70867b..608bb79059 100644 --- a/testsuite/surfaces/fault.cpp +++ b/testsuite/surfaces/fault.cpp @@ -44,6 +44,7 @@ using regina::Example; using regina::NormalListFlags; +using regina::NormalSurface; using regina::NormalSurfaces; using regina::NS_EMBEDDED_ONLY; using regina::NS_FUNDAMENTAL; @@ -385,14 +386,18 @@ class FaultTest : public CppUnit::TestFixture { Triangulation<3>* verifyAllVertexKleinBottlesInessential(Triangulation<3>* tri, const std::string& triName){ + Triangulation<3>* working = new Triangulation<3>(tri->isoSig()); NormalSurfaces* s = NormalSurfaces::enumerate( - tri, NS_QUAD, NS_VERTEX_EMBEDDED); + working, NS_QUAD, NS_VERTEX_EMBEDDED); bool found_essential_klein_bottle = false; - for (unsigned i = 0; i < s->size(); ++i) - if (s->surface(i)->isEssentialKleinBottle()){ + unsigned i = 0; + for (; i < s->size(); ++i){ + const NormalSurface* surf = s->surface(i); + if (surf->isEssentialKleinBottle()){ found_essential_klein_bottle = true; break; } + } delete s; if (found_essential_klein_bottle) CPPUNIT_FAIL((triName + @@ -453,18 +458,23 @@ class FaultTest : public CppUnit::TestFixture { int p = 3; int q = 2; + ostringstream oss; while (p <= 34){ tri = Example<3>::lst(p,q); delete verifyAllVertexKleinBottlesInessential(tri, "Solid torus"); tri = Example<3>::lens(p,q); - ostringstream oss; oss << "L(" << p << "," << q << ")" ; delete verifyAllVertexKleinBottlesInessential(tri, oss.str()); + oss.clear(); + oss.str(""); tri = smallSFS(2,1,2,1,p,-q); + oss << "SFS [S2, (2,1,2,1," << p << "," << q << ")]"; delete verifyAllVertexKleinBottlesInessential(tri, oss.str() ); - + oss.clear(); + oss.str(""); + p = p + q; q = p - q; } From 69ae208507a3858684b6101d8440f12deaf10c19 Mon Sep 17 00:00:00 2001 From: "Robert C. Haraway, III" Date: Thu, 29 Oct 2020 14:36:59 -0500 Subject: [PATCH 14/31] Add unit tests for isSolidTorusAnnulus Add unit tests for the `isSolidTorusAnnulus` member function of the `NormalSurface` class. --- testsuite/surfaces/fault.cpp | 190 ++++++++++++++++++++++++++++++++++- 1 file changed, 188 insertions(+), 2 deletions(-) diff --git a/testsuite/surfaces/fault.cpp b/testsuite/surfaces/fault.cpp index 608bb79059..7866c27171 100644 --- a/testsuite/surfaces/fault.cpp +++ b/testsuite/surfaces/fault.cpp @@ -710,10 +710,196 @@ class FaultTest : public CppUnit::TestFixture { delete verifyHasVertexEssentialTorus(tri, "Doubled trefoil knot exterior"); } - void isSolidTorusAnnulus() { - CPPUNIT_FAIL("Not implemented yet"); + + Triangulation<3>* verifyNoVertexSolidTorusAnnulus(Triangulation<3>* tri, + const std::string& triName){ + NormalSurfaces* s = NormalSurfaces::enumerate( + tri, NS_QUAD, NS_VERTEX_EMBEDDED); + bool found_solid_torus_annulus = false; + for (unsigned i = 0; i < s->size(); ++i) + if (s->surface(i)->isSolidTorusAnnulus()){ + found_solid_torus_annulus = true; + break; + } + delete s; + if (found_solid_torus_annulus) + CPPUNIT_FAIL((triName + + " has a \"solid torus annulus.\"").c_str()); + return tri; + } + + Triangulation<3>* verifyHasVertexSolidTorusAnnulus(Triangulation<3>* tri, + const std::string& triName){ + NormalSurfaces* s = NormalSurfaces::enumerate( + tri, NS_QUAD, NS_VERTEX_EMBEDDED); + bool found_solid_torus_annulus = false; + for (unsigned i = 0; i < s->size(); ++i) + if (s->surface(i)->isSolidTorusAnnulus()){ + found_solid_torus_annulus = true; + break; + } + delete s; + if (!found_solid_torus_annulus) + CPPUNIT_FAIL(("No solid torus annulus found in " + + triName + ".").c_str()); + return tri; } + + void isSolidTorusAnnulus() { + Triangulation<3>* tri; + + // Closed manifolds + tri = Example<3>::threeSphere(); + delete verifyNoVertexSolidTorusAnnulus(tri, "Minimal 3-sphere"); + tri = Example<3>::simplicialSphere(); + delete verifyNoVertexSolidTorusAnnulus(tri, "Pentachoron boundary 3-sphere"); + tri = Example<3>::poincareHomologySphere(); + delete verifyNoVertexSolidTorusAnnulus(tri, "Poincare homology sphere"); + tri = Example<3>::weeks(); + delete verifyNoVertexSolidTorusAnnulus(tri, "Weeks-Matveev-Fomenko manifold"); + int p = 3; + int q = 2; + while (p < 1000){ + tri = Example<3>::lens(p,q); + delete verifyNoVertexSolidTorusAnnulus(tri, "Fibonacci lens space"); + tri = smallSFS(2,1,2,1,p,-q); + ostringstream oss; + oss << "SFS [S2: (2,1)(2,1)(" << p << "," << -q << ")]"; + delete verifyNoVertexSolidTorusAnnulus(tri, oss.str()); + p = p + q; + q = p - q; + } + + // Small Seifert fibered spaces... +#define VERIFY_SMALL_SFS_ANANNULAR(a0,b0,a1,b1,a2,b2) \ + tri = smallSFS(a0,b0,a1,b1,a2,b2); \ + tri->intelligentSimplify(); \ + delete verifyNoVertexSolidTorusAnnulus(tri, "SFS [S2: (a0,b0) (a1,b1) (a2,b2)]") + + // ... of positive orbifold Euler characteristic ... + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,1,3,-2); + + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,1,3,-1); + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,1,4,-3); + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,1,5,-4); + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,2,3,-1); + + /* + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,1,3,1); + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,1,3,2); + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,1,4,-1); + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,1,5,-3); + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,1,5,-2); + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,2,3,2); + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,2,4,-3); + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,2,4,-1); + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,2,5,-3); + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,2,5,-2); + */ + + // ...and of negative orbifold Euler characteristic.... + /* + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,1,7,-6); + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,1,7,-5); + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,1,7,-5); + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,1,7,-4); + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,1,7,-2); + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,1,8,-5); + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,1,8,-3); + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,2,7,-5); + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,2,7,-4); + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,2,7,-3); + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,2,7,-2); + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,2,8,-5); + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,2,8,-3); + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,2,8,-3); + VERIFY_SMALL_SFS_ANANNULAR(2,1,4,1,5,-4); + VERIFY_SMALL_SFS_ANANNULAR(2,1,4,1,5,-3); + VERIFY_SMALL_SFS_ANANNULAR(2,1,4,1,5,-2); + VERIFY_SMALL_SFS_ANANNULAR(2,1,4,3,5,-3); + VERIFY_SMALL_SFS_ANANNULAR(2,1,4,3,5,-2); + */ + VERIFY_SMALL_SFS_ANANNULAR(2,1,5,2,5,-3); + VERIFY_SMALL_SFS_ANANNULAR(2,1,5,2,5,-2); + VERIFY_SMALL_SFS_ANANNULAR(2,1,5,3,5,-2); + + VERIFY_SMALL_SFS_ANANNULAR(3,1,3,1,4,-3); + VERIFY_SMALL_SFS_ANANNULAR(3,1,3,1,4,-1); + VERIFY_SMALL_SFS_ANANNULAR(3,1,3,1,5,-3); + VERIFY_SMALL_SFS_ANANNULAR(3,1,3,1,5,-2); + /* + VERIFY_SMALL_SFS_ANANNULAR(3,1,3,2,4,-3); + VERIFY_SMALL_SFS_ANANNULAR(3,1,3,2,4,-1); + VERIFY_SMALL_SFS_ANANNULAR(3,1,3,2,5,-3); + VERIFY_SMALL_SFS_ANANNULAR(3,1,3,2,5,-2); + VERIFY_SMALL_SFS_ANANNULAR(3,2,3,2,4,-3); + VERIFY_SMALL_SFS_ANANNULAR(3,2,3,2,4,-1); + VERIFY_SMALL_SFS_ANANNULAR(3,2,3,2,5,-3); + VERIFY_SMALL_SFS_ANANNULAR(3,2,3,2,5,-2); + */ + + // Bounded manifolds + + tri = Example<3>::ball(); + delete verifyNoVertexSolidTorusAnnulus(tri, "One tetrahedron ball"); + + // The following is not anannular, + // but it does not decompose along an annulus into solid tori. + tri = Example<3>::cuspedGenusTwoTorus(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyNoVertexSolidTorusAnnulus( + tri, "Trivial I-bundle over genus two surface"); + + tri = Example<3>::figureEight(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyNoVertexSolidTorusAnnulus(tri, "Figure eight knot exterior"); + + tri = Example<3>::whiteheadLink(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyNoVertexSolidTorusAnnulus(tri, "Whitehead link exterior"); + tri = Example<3>::gieseking(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyNoVertexSolidTorusAnnulus(tri, "Gieseking manifold"); + + tri = Example<3>::solidKleinBottle(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyNoVertexSolidTorusAnnulus(tri, "Solid Klein bottle"); + + p = 3; + q = 2; + while (p < 1000){ + tri = Example<3>::lst(p,q); + delete verifyNoVertexSolidTorusAnnulus(tri, "Solid torus"); + p = p + q; + q = p - q; + } + // Manifolds with solid torus annuli + + // Although the trefoil knot does admit a solid torus annulus, + // this annulus double covers a Mobius band. + // So it is possible for there not to be a vertex such annulus. + // Even so, the following triangulation does have such an annulus. + /* + tri = Example<3>::trefoil(); + tri->idealToFinite(); + tri->intelligentSimplify(); + */ + // For consistency we remove the call to intelligentSimplify. + tri = new Triangulation<3>("fLHPccdeeeqcieh"); + delete verifyHasVertexSolidTorusAnnulus(tri, "Trefoil knot exterior"); + + tri = new Triangulation<3>("gLKjMcacdeffjkxnqs"); + delete verifyHasVertexSolidTorusAnnulus(tri, "SFS D: (3,1) (5,-2)"); + + tri = new Triangulation<3>("mHLyMzMzMcbcdefghijklldwuxhqxhqxhw"); + delete verifyHasVertexSolidTorusAnnulus(tri, "SFS D: (2,1) (144,-89)"); + } }; void addFaultFinding(CppUnit::TextUi::TestRunner& runner) { From 0e2b8bd1d72d46cb574e527d128d71da1f004e5a Mon Sep 17 00:00:00 2001 From: "Robert C. Haraway, III" Date: Thu, 29 Oct 2020 14:57:00 -0500 Subject: [PATCH 15/31] Add fault tests Add `separates`, `isEssentialSphere`, `isEssentialKleinBottle`, `isEssentialTorus`, and `isSolidTorusAnnulus` member functions to `NormalSurface` class, with unit tests, Python bindings, and documentation. --- engine/surfaces/fault.cpp | 24 +++++++++++++++++++++++- testsuite/surfaces/fault.cpp | 18 ++++++++++-------- 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/engine/surfaces/fault.cpp b/engine/surfaces/fault.cpp index 036ab06051..0facbbcccb 100644 --- a/engine/surfaces/fault.cpp +++ b/engine/surfaces/fault.cpp @@ -161,7 +161,29 @@ namespace regina { } bool NormalSurface::isSolidTorusAnnulus() const{ - return false; + if (!(isConnected() + && isCompact() + && isOrientable() + && hasRealBoundary() + && eulerChar() == 0)) + return false; + + Triangulation<3>* cut_up = cutAlong(); + cut_up->intelligentSimplify(); + + bool solid_torus; + if (cut_up->isConnected()) + solid_torus = cut_up->isSolidTorus(); + else { + cut_up->splitIntoComponents(); + Triangulation<3>* L = (Triangulation<3>*)(cut_up->firstChild()); + Triangulation<3>* R = (Triangulation<3>*)(L->nextSibling()); + solid_torus = L->isSolidTorus() && R->isSolidTorus(); + delete L; + delete R; + } + delete cut_up; + return solid_torus; } } diff --git a/testsuite/surfaces/fault.cpp b/testsuite/surfaces/fault.cpp index 7866c27171..fdb802f0d9 100644 --- a/testsuite/surfaces/fault.cpp +++ b/testsuite/surfaces/fault.cpp @@ -865,22 +865,24 @@ class FaultTest : public CppUnit::TestFixture { tri->intelligentSimplify(); delete verifyNoVertexSolidTorusAnnulus(tri, "Gieseking manifold"); - tri = Example<3>::solidKleinBottle(); - tri->idealToFinite(); - tri->intelligentSimplify(); - delete verifyNoVertexSolidTorusAnnulus(tri, "Solid Klein bottle"); - + // Manifolds with solid torus annuli + p = 3; q = 2; while (p < 1000){ tri = Example<3>::lst(p,q); - delete verifyNoVertexSolidTorusAnnulus(tri, "Solid torus"); + ostringstream oss; + oss << "Layered solid torus (" << p << "," << q << ")"; + delete verifyHasVertexSolidTorusAnnulus(tri, oss.str()); p = p + q; q = p - q; } - - // Manifolds with solid torus annuli + tri = Example<3>::solidKleinBottle(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyHasVertexSolidTorusAnnulus(tri, "Solid Klein bottle"); + // Although the trefoil knot does admit a solid torus annulus, // this annulus double covers a Mobius band. // So it is possible for there not to be a vertex such annulus. From e77c5a4372efd8924a6146ff199b412ee40ae54b Mon Sep 17 00:00:00 2001 From: "Robert C. Haraway, III" Date: Wed, 21 Oct 2020 15:55:22 -0500 Subject: [PATCH 16/31] Implemented scaffolding for fault finding Implemented CMake and CppUnit scaffolding for fault finding; implemented naive separation testing for surfaces. --- engine/surfaces/CMakeLists.txt | 1 + engine/surfaces/fault.cpp | 59 +++++++++ engine/surfaces/normalsurface.h | 17 +++ testsuite/surfaces/CMakeLists.txt | 1 + testsuite/surfaces/fault.cpp | 200 ++++++++++++++++++++++++++++++ testsuite/surfaces/testsurfaces.h | 1 + testsuite/testsuite.cpp | 1 + 7 files changed, 280 insertions(+) create mode 100644 engine/surfaces/fault.cpp create mode 100644 testsuite/surfaces/fault.cpp diff --git a/engine/surfaces/CMakeLists.txt b/engine/surfaces/CMakeLists.txt index 2643812f46..58e9d7f291 100644 --- a/engine/surfaces/CMakeLists.txt +++ b/engine/surfaces/CMakeLists.txt @@ -10,6 +10,7 @@ SET ( FILES disctype enumerator enumfilter + fault links normalsurface normalsurfaces diff --git a/engine/surfaces/fault.cpp b/engine/surfaces/fault.cpp new file mode 100644 index 0000000000..684e487910 --- /dev/null +++ b/engine/surfaces/fault.cpp @@ -0,0 +1,59 @@ + +/************************************************************************** + * * + * Regina - A Normal Surface Theory Calculator * + * Computational Engine * + * * + * This file: Copyright (c) 2020, Robert C. Haraway, III * + * For further details contact Robert (bobbycyiii@fastmail.com). * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * As an exception, when this program is distributed through (i) the * + * App Store by Apple Inc.; (ii) the Mac App Store by Apple Inc.; or * + * (iii) Google Play by Google Inc., then that store may impose any * + * digital rights management, device limits and/or redistribution * + * restrictions that are required by its terms of service. * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * General Public License for more details. * + * * + * You should have received a copy of the GNU General Public * + * License along with this program; if not, write to the Free * + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + * * + **************************************************************************/ + + +#include "surfaces/normalsurface.h" +#include "triangulation/dim3.h" + +namespace regina { + bool NormalSurface::separates() const{ + int tri_cpts = this->triangulation()->countComponents(); + Triangulation<3>* cut_up = this->cutAlong(); + cut_up->intelligentSimplify(); + int new_cpts = cut_up->countComponents(); + delete cut_up; + return tri_cpts < new_cpts; + } + + bool NormalSurface::isEssentialSphere() const{ + return false; + } + + bool NormalSurface::isEssentialTorus() const{ + return false; + } + + bool NormalSurface::isSolidTorusAnnulus() const{ + return false; + } + +} diff --git a/engine/surfaces/normalsurface.h b/engine/surfaces/normalsurface.h index 98fc4098c3..155e053540 100644 --- a/engine/surfaces/normalsurface.h +++ b/engine/surfaces/normalsurface.h @@ -1443,6 +1443,23 @@ class REGINA_API NormalSurface : public ShortOutput { */ bool isIncompressible() const; + /** + * Determines whether or not cutting along the surface + * yields a new component. + */ + bool separates() const; + + bool isEssentialSphere() const; + + bool isEssentialTorus() const; + + /** + * Determines whether or not this surface is an annulus + * cutting along which yields two solid tori. + * This is easier to check than whether or not the annulus is essential. + */ + bool isSolidTorusAnnulus() const; + /** * Cuts the associated triangulation along this surface and * returns a newly created resulting triangulation. diff --git a/testsuite/surfaces/CMakeLists.txt b/testsuite/surfaces/CMakeLists.txt index 15b6922116..d9f5d7013e 100644 --- a/testsuite/surfaces/CMakeLists.txt +++ b/testsuite/surfaces/CMakeLists.txt @@ -2,6 +2,7 @@ # Files to compile SET ( FILES + fault incompressible normalsurfaces ) diff --git a/testsuite/surfaces/fault.cpp b/testsuite/surfaces/fault.cpp new file mode 100644 index 0000000000..2c1c80cdfe --- /dev/null +++ b/testsuite/surfaces/fault.cpp @@ -0,0 +1,200 @@ + +/************************************************************************** + * * + * Regina - A Normal Surface Theory Calculator * + * Test Suite * + * * + * Copyright (c) 2020, Robert C. Haraway, III. * + * For further details contact Robert Haraway (bobbycyiii@fastmail.com). * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * As an exception, when this program is distributed through (i) the * + * App Store by Apple Inc.; (ii) the Mac App Store by Apple Inc.; or * + * (iii) Google Play by Google Inc., then that store may impose any * + * digital rights management, device limits and/or redistribution * + * restrictions that are required by its terms of service. * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * General Public License for more details. * + * * + * You should have received a copy of the GNU General Public * + * License along with this program; if not, write to the Free * + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + * * + **************************************************************************/ + +#include +#include "surfaces/normalsurface.h" +#include "surfaces/normalsurfaces.h" +#include "triangulation/dim3.h" +#include "triangulation/example3.h" +#include "testsuite/dim3/testtriangulation.h" + + +using regina::Example; +using regina::NormalSurfaces; +using regina::Triangulation; + +class FaultTest : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(FaultTest); + + CPPUNIT_TEST(separates); + CPPUNIT_TEST(isEssentialSphere); + CPPUNIT_TEST(isEssentialTorus); + CPPUNIT_TEST(isSolidTorusAnnulus); + + CPPUNIT_TEST_SUITE_END(); + +public: + void setUp() { + } + + void tearDown() { + } + + Triangulation<3>* verifyAllSeparating(Triangulation<3>* tri, + const std::string& triName) { + NormalSurfaces* s = NormalSurfaces::enumerate( + tri, regina::NS_STANDARD, regina::NS_EMBEDDED_ONLY); + bool nonseparating_known = false; + for (unsigned i = 0; i < s->size(); ++i) + if (!(s->surface(i)->separates())){ + nonseparating_known = true; + break; + } + delete s; + if (nonseparating_known) + CPPUNIT_FAIL(("A surface in " + triName + + " is computed to be nonseparating.").c_str()); + return tri; + } + + Triangulation<3>* verifyHasNonSeparating(Triangulation<3>* tri, + const std::string& triName){ + NormalSurfaces* s = NormalSurfaces::enumerate( + tri, regina::NS_STANDARD, regina::NS_EMBEDDED_ONLY); + bool all_known_surfaces_separating = true; + for (unsigned i = 0; i < s->size(); ++i) + if (!(s->surface(i)->separates())){ + all_known_surfaces_separating = false; + break; + } + delete s; + if (all_known_surfaces_separating) + CPPUNIT_FAIL(("No surfaces in " + triName + + " were computed to be nonseparating.").c_str()); + return tri; + } + + void separates() { + Triangulation<3>* tri; + + // Manifolds without nonseparating surfaces + + tri = Example<3>::threeSphere(); + delete verifyAllSeparating(tri, "Minimal 3-sphere"); + + tri = Example<3>::simplicialSphere(); + delete verifyAllSeparating(tri, "Pentachoron boundary 3-sphere"); + + tri = Example<3>::ball(); + delete verifyAllSeparating(tri, "One-tetrahedron ball"); + + int p = 3; + int q = 2; + while (p < 1000){ + if (p % 2 != 0){ + tri = Example<3>::lens(p,q); + delete verifyAllSeparating(tri, "Lens space with odd p"); + } + p = p + q; + q = p - q; + } + + tri = Example<3>::poincareHomologySphere(); + delete verifyAllSeparating(tri, "Poincare homology sphere"); + + tri = Example<3>::weeks(); + delete verifyAllSeparating(tri, "Weeks-Matveev-Fomenko manifold"); + + + // Manifolds with nonseparating surfaces + + tri = Example<3>::s2xs1(); + delete verifyHasNonSeparating(tri, "S2xS1"); + + tri = Example<3>::rp2xs1(); + delete verifyHasNonSeparating(tri, "RP2xS1"); + + tri = Example<3>::rp3rp3(); + delete verifyHasNonSeparating(tri, "RP3#RP3"); + + tri = Example<3>::smallClosedNonOrblHyperbolic(); + delete verifyHasNonSeparating(tri, "Smallest known closed nonorientable hyperbolic"); + + p = 3; + q = 2; + while (p < 1000){ + tri = Example<3>::lst(p,q); + delete verifyHasNonSeparating(tri, "Solid torus"); + if (p % 2 == 0){ + tri = Example<3>::lens(p,q); + delete verifyHasNonSeparating(tri, "Lens space with even p"); + } + p = p + q; + q = p - q; + } + + tri = Example<3>::solidKleinBottle(); + delete verifyHasNonSeparating(tri, "Solid Klein bottle"); + + tri = Example<3>::figureEight(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyHasNonSeparating(tri, "Figure eight"); + + tri = Example<3>::trefoil(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyHasNonSeparating(tri, "Trefoil"); + + tri = Example<3>::whiteheadLink(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyHasNonSeparating(tri, "Whitehead link"); + + tri = Example<3>::gieseking(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyHasNonSeparating(tri, "Gieseking manifold"); + + tri = Example<3>::cuspedGenusTwoTorus(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyHasNonSeparating(tri, "Genus two surface x I"); + } + + void isEssentialSphere() { + CPPUNIT_FAIL("Not implemented yet"); + } + + void isEssentialTorus() { + CPPUNIT_FAIL("Not implemented yet"); + } + + void isSolidTorusAnnulus() { + CPPUNIT_FAIL("Not implemented yet"); + } + +}; + +void addFaultFinding(CppUnit::TextUi::TestRunner& runner) { + runner.addTest(FaultTest::suite()); +} diff --git a/testsuite/surfaces/testsurfaces.h b/testsuite/surfaces/testsurfaces.h index 01d8d121a3..4c4ed25124 100644 --- a/testsuite/surfaces/testsurfaces.h +++ b/testsuite/surfaces/testsurfaces.h @@ -44,4 +44,5 @@ void addNormalSurfaces(CppUnit::TextUi::TestRunner& runner); void addIncompressible(CppUnit::TextUi::TestRunner& runner); +void addFaultFinding(CppUnit::TextUi::TestRunner& runner); diff --git a/testsuite/testsuite.cpp b/testsuite/testsuite.cpp index 735e38b362..92d97ab277 100644 --- a/testsuite/testsuite.cpp +++ b/testsuite/testsuite.cpp @@ -159,6 +159,7 @@ bool populateTests(CppUnit::TextTestRunner& runner, int argc, char* argv[]) { // Surfaces: sets.insert(std::make_pair("normalsurfaces", &addNormalSurfaces)); sets.insert(std::make_pair("incompressible", &addIncompressible)); + sets.insert(std::make_pair("faultfinding", &addFaultFinding)); // Angle structures: sets.insert(std::make_pair("anglestructures", &addAngleStructures)); From f032fb3e5e1f6376066f045132dc22ecb3e72b2f Mon Sep 17 00:00:00 2001 From: "Robert C. Haraway, III" Date: Wed, 28 Oct 2020 10:06:38 -0500 Subject: [PATCH 17/31] Fixed separation testing Fixed the tests for separating surfaces in lens spaces. --- engine/surfaces/fault.cpp | 4 +- testsuite/surfaces/fault.cpp | 130 +++++++++++++++++++++++++++-------- 2 files changed, 105 insertions(+), 29 deletions(-) diff --git a/engine/surfaces/fault.cpp b/engine/surfaces/fault.cpp index 684e487910..89c2047cf3 100644 --- a/engine/surfaces/fault.cpp +++ b/engine/surfaces/fault.cpp @@ -36,8 +36,8 @@ namespace regina { bool NormalSurface::separates() const{ - int tri_cpts = this->triangulation()->countComponents(); - Triangulation<3>* cut_up = this->cutAlong(); + int tri_cpts = triangulation()->countComponents(); + Triangulation<3>* cut_up = cutAlong(); cut_up->intelligentSimplify(); int new_cpts = cut_up->countComponents(); delete cut_up; diff --git a/testsuite/surfaces/fault.cpp b/testsuite/surfaces/fault.cpp index 2c1c80cdfe..fdb181f190 100644 --- a/testsuite/surfaces/fault.cpp +++ b/testsuite/surfaces/fault.cpp @@ -31,6 +31,9 @@ **************************************************************************/ #include +#include +#include "surfaces/normalcoords.h" +#include "surfaces/normalflags.h" #include "surfaces/normalsurface.h" #include "surfaces/normalsurfaces.h" #include "triangulation/dim3.h" @@ -39,9 +42,18 @@ using regina::Example; +using regina::NormalListFlags; using regina::NormalSurfaces; +using regina::NS_EMBEDDED_ONLY; +using regina::NS_FUNDAMENTAL; +using regina::NS_QUAD; +using regina::NS_VERTEX; using regina::Triangulation; +using std::ostringstream; + +NormalListFlags NS_VERTEX_EMBEDDED = (NormalListFlags)(NS_VERTEX & NS_EMBEDDED_ONLY); + class FaultTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(FaultTest); @@ -59,10 +71,46 @@ class FaultTest : public CppUnit::TestFixture { void tearDown() { } - Triangulation<3>* verifyAllSeparating(Triangulation<3>* tri, + + Triangulation<3>* verifyAllVertexSeparating(Triangulation<3>* tri, + const std::string& triName) { + NormalSurfaces* s = NormalSurfaces::enumerate( + tri, NS_QUAD, NS_VERTEX_EMBEDDED); + bool nonseparating_known = false; + for (unsigned i = 0; i < s->size(); ++i) + if (!(s->surface(i)->separates())){ + nonseparating_known = true; + break; + } + delete s; + if (nonseparating_known) + CPPUNIT_FAIL(("A surface in " + triName + + " is computed to be nonseparating.").c_str()); + return tri; + } + + Triangulation<3>* verifyHasVertexNonSeparating(Triangulation<3>* tri, + const std::string& triName){ + NormalSurfaces* s = NormalSurfaces::enumerate( + tri, NS_QUAD, NS_VERTEX_EMBEDDED); + bool all_known_surfaces_separating = true; + for (unsigned i = 0; i < s->size(); ++i) + if (!(s->surface(i)->separates())){ + all_known_surfaces_separating = false; + break; + } + delete s; + if (all_known_surfaces_separating) + CPPUNIT_FAIL(("No surfaces in " + triName + + " were computed to be nonseparating.").c_str()); + return tri; + } + + /* + Triangulation<3>* verifyAllFundamentalSeparating(Triangulation<3>* tri, const std::string& triName) { NormalSurfaces* s = NormalSurfaces::enumerate( - tri, regina::NS_STANDARD, regina::NS_EMBEDDED_ONLY); + tri, NS_QUAD, NS_VERTEX_EMBEDDED); bool nonseparating_known = false; for (unsigned i = 0; i < s->size(); ++i) if (!(s->surface(i)->separates())){ @@ -75,11 +123,12 @@ class FaultTest : public CppUnit::TestFixture { " is computed to be nonseparating.").c_str()); return tri; } + */ - Triangulation<3>* verifyHasNonSeparating(Triangulation<3>* tri, + Triangulation<3>* verifyHasFundamentalNonSeparating(Triangulation<3>* tri, const std::string& triName){ NormalSurfaces* s = NormalSurfaces::enumerate( - tri, regina::NS_STANDARD, regina::NS_EMBEDDED_ONLY); + tri, NS_QUAD, NS_FUNDAMENTAL); bool all_known_surfaces_separating = true; for (unsigned i = 0; i < s->size(); ++i) if (!(s->surface(i)->separates())){ @@ -93,96 +142,123 @@ class FaultTest : public CppUnit::TestFixture { return tri; } + /** + * All we can conclude from S not being a vertex surface + * is that there is some equation of the form + * + * nS + T = X + Y + * + * for some natural n > 0, trivial surface T, and normal surfaces X, Y. + * Even if there is a nonseparating surface, + * we cannot conclude that some vertex of its support is also nonseparating, + * because the above n might be even, in which case nS is separating. + * So if there is a nonseparating surface in a triangulation, + * nevertheless there might not be a nonseparating vertex-normal such surface. + * + * For example, L(34,13) has H_2(L(34,13),Z_2) = Z_2. + * So L(34,13) admits a nonseparating surface. + * But fLAMcbcbdeehxwqhr = L(34,13) has no nonseparating quad-vertex surface. + * + * However, there must be a nonseparating fundamental normal such surface. + * For instance, in fLAMcbcbdeehxwqhr there is a fundamental P2#P2#P2. + * + * Thus we should have backup tests for manifolds + * with no Z second homology but with Z_2 second homology. + */ + void separates() { Triangulation<3>* tri; // Manifolds without nonseparating surfaces tri = Example<3>::threeSphere(); - delete verifyAllSeparating(tri, "Minimal 3-sphere"); + delete verifyAllVertexSeparating(tri, "Minimal 3-sphere"); tri = Example<3>::simplicialSphere(); - delete verifyAllSeparating(tri, "Pentachoron boundary 3-sphere"); + delete verifyAllVertexSeparating(tri, "Pentachoron boundary 3-sphere"); tri = Example<3>::ball(); - delete verifyAllSeparating(tri, "One-tetrahedron ball"); + delete verifyAllVertexSeparating(tri, "One-tetrahedron ball"); int p = 3; int q = 2; - while (p < 1000){ + while (p <= 34){ if (p % 2 != 0){ tri = Example<3>::lens(p,q); - delete verifyAllSeparating(tri, "Lens space with odd p"); + ostringstream oss; + oss << "L(" << p << "," << q << ")" ; + delete verifyAllVertexSeparating(tri, oss.str()); } p = p + q; q = p - q; } tri = Example<3>::poincareHomologySphere(); - delete verifyAllSeparating(tri, "Poincare homology sphere"); + delete verifyAllVertexSeparating(tri, "Poincare homology sphere"); tri = Example<3>::weeks(); - delete verifyAllSeparating(tri, "Weeks-Matveev-Fomenko manifold"); - + delete verifyAllVertexSeparating(tri, "Weeks-Matveev-Fomenko manifold"); // Manifolds with nonseparating surfaces tri = Example<3>::s2xs1(); - delete verifyHasNonSeparating(tri, "S2xS1"); + delete verifyHasVertexNonSeparating(tri, "S2xS1"); tri = Example<3>::rp2xs1(); - delete verifyHasNonSeparating(tri, "RP2xS1"); + delete verifyHasVertexNonSeparating(tri, "RP2xS1"); tri = Example<3>::rp3rp3(); - delete verifyHasNonSeparating(tri, "RP3#RP3"); + delete verifyHasVertexNonSeparating(tri, "RP3#RP3"); tri = Example<3>::smallClosedNonOrblHyperbolic(); - delete verifyHasNonSeparating(tri, "Smallest known closed nonorientable hyperbolic"); + delete verifyHasVertexNonSeparating(tri, "Smallest known closed nonorientable hyperbolic"); p = 3; q = 2; - while (p < 1000){ + while (p <= 34){ tri = Example<3>::lst(p,q); - delete verifyHasNonSeparating(tri, "Solid torus"); + delete verifyHasVertexNonSeparating(tri, "Solid torus"); if (p % 2 == 0){ tri = Example<3>::lens(p,q); - delete verifyHasNonSeparating(tri, "Lens space with even p"); + ostringstream oss; + oss << "L(" << p << "," << q << ")" ; + delete verifyHasFundamentalNonSeparating(tri, oss.str()); } p = p + q; q = p - q; } tri = Example<3>::solidKleinBottle(); - delete verifyHasNonSeparating(tri, "Solid Klein bottle"); + delete verifyHasVertexNonSeparating(tri, "Solid Klein bottle"); tri = Example<3>::figureEight(); tri->idealToFinite(); tri->intelligentSimplify(); - delete verifyHasNonSeparating(tri, "Figure eight"); + delete verifyHasVertexNonSeparating(tri, "Figure eight"); tri = Example<3>::trefoil(); tri->idealToFinite(); tri->intelligentSimplify(); - delete verifyHasNonSeparating(tri, "Trefoil"); + delete verifyHasVertexNonSeparating(tri, "Trefoil"); tri = Example<3>::whiteheadLink(); tri->idealToFinite(); tri->intelligentSimplify(); - delete verifyHasNonSeparating(tri, "Whitehead link"); + delete verifyHasVertexNonSeparating(tri, "Whitehead link"); tri = Example<3>::gieseking(); tri->idealToFinite(); tri->intelligentSimplify(); - delete verifyHasNonSeparating(tri, "Gieseking manifold"); + delete verifyHasVertexNonSeparating(tri, "Gieseking manifold"); tri = Example<3>::cuspedGenusTwoTorus(); tri->idealToFinite(); tri->intelligentSimplify(); - delete verifyHasNonSeparating(tri, "Genus two surface x I"); + delete verifyHasVertexNonSeparating(tri, "Genus two surface x I"); } - + void isEssentialSphere() { - CPPUNIT_FAIL("Not implemented yet"); + CPPUNIT_FAIL("Not implemented yet"); } void isEssentialTorus() { From 38e5c10509fd8636e7a67aafa21bf4c64c0f45a3 Mon Sep 17 00:00:00 2001 From: "Robert C. Haraway, III" Date: Wed, 28 Oct 2020 10:34:10 -0500 Subject: [PATCH 18/31] Finished tests for essential spheres Finished implementing tests for `isEssentialSphere` member function of `NormalSurface` class. --- testsuite/surfaces/fault.cpp | 120 ++++++++++++++++++++++++++++++++++- 1 file changed, 119 insertions(+), 1 deletion(-) diff --git a/testsuite/surfaces/fault.cpp b/testsuite/surfaces/fault.cpp index fdb181f190..f70fe9e948 100644 --- a/testsuite/surfaces/fault.cpp +++ b/testsuite/surfaces/fault.cpp @@ -256,9 +256,127 @@ class FaultTest : public CppUnit::TestFixture { tri->intelligentSimplify(); delete verifyHasVertexNonSeparating(tri, "Genus two surface x I"); } + + + Triangulation<3>* verifyAllVertexSpheresTrivial(Triangulation<3>* tri, + const std::string& triName){ + NormalSurfaces* s = NormalSurfaces::enumerate( + tri, NS_QUAD, NS_VERTEX_EMBEDDED); + bool found_essential_sphere = false; + for (unsigned i = 0; i < s->size(); ++i) + if (s->surface(i)->isEssentialSphere()){ + found_essential_sphere = true; + break; + } + delete s; + if (found_essential_sphere) + CPPUNIT_FAIL(("The irreducible manifold " + triName + + " was computed to have an essential sphere.").c_str()); + return tri; + } + + Triangulation<3>* verifyHasVertexEssentialSphere(Triangulation<3>* tri, + const std::string& triName){ + NormalSurfaces* s = NormalSurfaces::enumerate( + tri, NS_QUAD, NS_VERTEX_EMBEDDED); + bool found_essential_sphere = false; + for (unsigned i = 0; i < s->size(); ++i) + if (s->surface(i)->isEssentialSphere()){ + found_essential_sphere = true; + break; + } + delete s; + if (!found_essential_sphere) + CPPUNIT_FAIL(("No vertex essential spheres were found in " + + triName + ".").c_str()); + return tri; + } + void isEssentialSphere() { - CPPUNIT_FAIL("Not implemented yet"); + Triangulation<3>* tri; + + // Closed irreducible manifolds + tri = Example<3>::threeSphere(); + delete verifyAllVertexSpheresTrivial(tri, "Minimal 3-sphere"); + tri = Example<3>::simplicialSphere(); + delete verifyAllVertexSpheresTrivial(tri, "Pentachoron boundary 3-sphere"); + tri = Example<3>::poincareHomologySphere(); + delete verifyAllVertexSpheresTrivial(tri, "Poincare homology sphere"); + tri = Example<3>::weeks(); + delete verifyAllVertexSpheresTrivial(tri, "Weeks-Matveev-Fomenko manifold"); + int p = 3; + int q = 2; + while (p < 1000){ + tri = Example<3>::lens(p,q); + ostringstream oss; + oss << "L(" << p << "," << q << ")"; + delete verifyAllVertexSpheresTrivial(tri, oss.str()); + p = p + q; + q = p - q; + } + + // Bounded irreducible manifolds + tri = Example<3>::ball(); + delete verifyAllVertexSpheresTrivial(tri, "One tetrahedron ball"); + tri = Example<3>::cuspedGenusTwoTorus(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyAllVertexSpheresTrivial( + tri, "Trivial I-bundle over genus two surface"); + tri = Example<3>::figureEight(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyAllVertexSpheresTrivial(tri, "Figure eight knot exterior"); + tri = Example<3>::trefoil(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyAllVertexSpheresTrivial(tri, "Trefoil knot exterior"); + tri = Example<3>::whiteheadLink(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyAllVertexSpheresTrivial(tri, "Whitehead link exterior"); + tri = Example<3>::gieseking(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyAllVertexSpheresTrivial(tri, "Gieseking manifold"); + tri = Example<3>::solidKleinBottle(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyAllVertexSpheresTrivial(tri, "Solid Klein bottle"); + p = 3; + q = 2; + while (p < 1000){ + tri = Example<3>::lst(p,q); + delete verifyAllVertexSpheresTrivial(tri, "Solid torus"); + p = p + q; + q = p - q; + } + + // The prime reducible manifold S^2 x S^1 + tri = Example<3>::s2xs1(); + delete verifyHasVertexEssentialSphere(tri, "S2xS1"); + + // RP3#RP3 has a reducing sphere, + // but in the given triangulation, there is no fundamental reducing sphere. + // Instead, there are two fundamental embedded one-sided RP2s + // that double to reducing spheres. + + tri = Example<3>::rp3rp3(); + delete verifyAllVertexSpheresTrivial(tri, "RP3#RP3"); + + p = 3; + q = 2; + while (p < 100){ + tri = Example<3>::lens(p,q); + tri->connectedSumWith(*tri); + tri->intelligentSimplify(); + ostringstream oss; + oss << "L(" << p << "," << q << ")"; + delete verifyHasVertexEssentialSphere(tri, oss.str() + " # " + oss.str()); + p = p + q; + q = p - q; + } } void isEssentialTorus() { From 8a89e449ea6413b2f4b176cbf7b5e53302bd5da5 Mon Sep 17 00:00:00 2001 From: "Robert C. Haraway, III" Date: Wed, 28 Oct 2020 10:45:19 -0500 Subject: [PATCH 19/31] Added Python bindings for fault tests Added Python bindings for the member functions `separates`, `isEssentialSphere`, `isEssentialTorus`, `isEssentialKleinBottle`, and `isSolidTorusAnnulus` of the `NormalSurface` class. --- python/surfaces/normalsurface.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/python/surfaces/normalsurface.cpp b/python/surfaces/normalsurface.cpp index 4cb428ad4b..929fdc54ec 100644 --- a/python/surfaces/normalsurface.cpp +++ b/python/surfaces/normalsurface.cpp @@ -117,6 +117,11 @@ void addNormalSurface(pybind11::module& m) { .def("countBoundaries", &NormalSurface::countBoundaries) .def("isCompressingDisc", &NormalSurface::isCompressingDisc, pybind11::arg("knownConnected") = false) + .def("separates", &NormalSurface::separates) + .def("isEssentialSphere", &NormalSurface::isEssentialSphere) + .def("isEssentialTorus", &NormalSurface::isEssentialTorus) + .def("isEssentialKleinBottle", &NormalSurface::isEssentialKleinBottle) + .def("isSolidTorusAnnulus", &NormalSurface::isSolidTorusAnnulus) .def("isIncompressible", &NormalSurface::isIncompressible) .def("cutAlong", &NormalSurface::cutAlong) .def("crush", &NormalSurface::crush) From fba8ec9290edb61f0dcdbff60f507801363e3a54 Mon Sep 17 00:00:00 2001 From: "Robert C. Haraway, III" Date: Wed, 28 Oct 2020 10:46:45 -0500 Subject: [PATCH 20/31] Finished scaffolding for fault tests Added header info for a missing function in `NormalSurface.h`. --- engine/surfaces/normalsurface.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/engine/surfaces/normalsurface.h b/engine/surfaces/normalsurface.h index 155e053540..77d9f14a3e 100644 --- a/engine/surfaces/normalsurface.h +++ b/engine/surfaces/normalsurface.h @@ -1451,6 +1451,8 @@ class REGINA_API NormalSurface : public ShortOutput { bool isEssentialSphere() const; + bool isEssentialKleinBottle() const; + bool isEssentialTorus() const; /** From 2e760c2def033688f0dde0955658c512b9c81bfe Mon Sep 17 00:00:00 2001 From: "Robert C. Haraway, III" Date: Wed, 28 Oct 2020 11:09:49 -0500 Subject: [PATCH 21/31] Essential sphere test Added unit tests and methods for `isEssentialSphere` member function of `NormalSurface` class. --- engine/surfaces/fault.cpp | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/engine/surfaces/fault.cpp b/engine/surfaces/fault.cpp index 89c2047cf3..e95c5a2577 100644 --- a/engine/surfaces/fault.cpp +++ b/engine/surfaces/fault.cpp @@ -45,6 +45,37 @@ namespace regina { } bool NormalSurface::isEssentialSphere() const{ + if (!(isConnected() + && isCompact() + && eulerChar() == 2)) + return false; + + int tri_cpts = triangulation()->countComponents(); + Triangulation<3>* cut_up = cutAlong(); + cut_up->intelligentSimplify(); + int new_cpts = cut_up->countComponents(); + if (tri_cpts >= new_cpts){ + delete cut_up; + return true; + } + + // Cap sphere boundary components. + cut_up->finiteToIdeal(); + cut_up->intelligentSimplify(); + cut_up->idealToFinite(); + cut_up->intelligentSimplify(); + + cut_up->splitIntoComponents(); + Triangulation<3>* L = (Triangulation<3>*) cut_up->firstChild(); + Triangulation<3>* R = (Triangulation<3>*) L->nextSibling(); + bool essential = !(L->isThreeSphere() || R->isThreeSphere()); + delete L; + delete R; + delete cut_up; + return essential; + } + + bool NormalSurface::isEssentialKleinBottle() const{ return false; } From b1d77fa58eeea8e006fcbe1bac2e833138c16a44 Mon Sep 17 00:00:00 2001 From: "Robert C. Haraway, III" Date: Wed, 28 Oct 2020 12:00:47 -0500 Subject: [PATCH 22/31] Implement tests for essential Klein bottle test Implemented tests for `isEssentialKleinBottle` member function of `NormalSurface` class. --- testsuite/surfaces/fault.cpp | 155 +++++++++++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) diff --git a/testsuite/surfaces/fault.cpp b/testsuite/surfaces/fault.cpp index f70fe9e948..039af05f6e 100644 --- a/testsuite/surfaces/fault.cpp +++ b/testsuite/surfaces/fault.cpp @@ -32,6 +32,7 @@ #include #include +#include "manifold/sfs.h" #include "surfaces/normalcoords.h" #include "surfaces/normalflags.h" #include "surfaces/normalsurface.h" @@ -48,6 +49,8 @@ using regina::NS_EMBEDDED_ONLY; using regina::NS_FUNDAMENTAL; using regina::NS_QUAD; using regina::NS_VERTEX; +using regina::SFSFibre; +using regina::SFSpace; using regina::Triangulation; using std::ostringstream; @@ -59,6 +62,7 @@ class FaultTest : public CppUnit::TestFixture { CPPUNIT_TEST(separates); CPPUNIT_TEST(isEssentialSphere); + CPPUNIT_TEST(isEssentialKleinBottle); CPPUNIT_TEST(isEssentialTorus); CPPUNIT_TEST(isSolidTorusAnnulus); @@ -378,6 +382,157 @@ class FaultTest : public CppUnit::TestFixture { q = p - q; } } + + Triangulation<3>* verifyAllVertexKleinBottlesInessential(Triangulation<3>* tri, + const std::string& triName){ + NormalSurfaces* s = NormalSurfaces::enumerate( + tri, NS_QUAD, NS_VERTEX_EMBEDDED); + bool found_essential_klein_bottle = false; + for (unsigned i = 0; i < s->size(); ++i) + if (s->surface(i)->isEssentialKleinBottle()){ + found_essential_klein_bottle = true; + break; + } + delete s; + if (found_essential_klein_bottle) + CPPUNIT_FAIL((triName + + " has an \"essential Klein bottle.\"").c_str()); + return tri; + } + + Triangulation<3>* verifyHasVertexEssentialKleinBottle(Triangulation<3>* tri, + const std::string& triName){ + NormalSurfaces* s = NormalSurfaces::enumerate( + tri, NS_QUAD, NS_VERTEX_EMBEDDED); + bool found_essential_klein_bottle = false; + for (unsigned i = 0; i < s->size(); ++i) + if (s->surface(i)->isEssentialKleinBottle()){ + found_essential_klein_bottle = true; + break; + } + delete s; + if (!found_essential_klein_bottle) + CPPUNIT_FAIL(("No vertex essential Klein bottle found in " + + triName + ".").c_str()); + return tri; + } + + + Triangulation<3>* smallSFS(long alpha0, long beta0, + long alpha1, long beta1, + long alpha2, long beta2){ + const SFSFibre* fiber0 = new SFSFibre(alpha0, beta0); + const SFSFibre* fiber1 = new SFSFibre(alpha1, beta1); + const SFSFibre* fiber2 = new SFSFibre(alpha2, beta2); + SFSpace* sfs = new SFSpace( + SFSpace::o1, 0, 0, 0, 0, 0); + sfs->insertFibre(*fiber0); + sfs->insertFibre(*fiber1); + sfs->insertFibre(*fiber2); + Triangulation<3>* sfs_tri = sfs->construct(); + delete sfs; + delete fiber0; + delete fiber1; + delete fiber2; + return sfs_tri; + } + + void isEssentialKleinBottle() { + Triangulation<3>* tri; + + // Manifolds with no essential Klein bottles + + tri = Example<3>::threeSphere(); + delete verifyAllVertexKleinBottlesInessential(tri, "Minimal 3-sphere"); + + tri = Example<3>::simplicialSphere(); + delete verifyAllVertexKleinBottlesInessential(tri, "Pentachoron boundary 3-sphere"); + + tri = Example<3>::ball(); + delete verifyAllVertexKleinBottlesInessential(tri, "One-tetrahedron ball"); + + int p = 3; + int q = 2; + while (p <= 34){ + if (p % 2 != 0){ + tri = Example<3>::lens(p,q); + ostringstream oss; + oss << "L(" << p << "," << q << ")" ; + delete verifyAllVertexKleinBottlesInessential(tri, oss.str()); + } + p = p + q; + q = p - q; + } + + tri = Example<3>::poincareHomologySphere(); + delete verifyAllVertexKleinBottlesInessential(tri, "Poincare homology sphere"); + + tri = Example<3>::weeks(); + delete verifyAllVertexKleinBottlesInessential(tri, "Weeks-Matveev-Fomenko manifold"); + + tri = Example<3>::s2xs1(); + delete verifyAllVertexKleinBottlesInessential(tri, "S2xS1"); + + tri = Example<3>::rp2xs1(); + delete verifyAllVertexKleinBottlesInessential(tri, "RP2xS1"); + + tri = Example<3>::rp3rp3(); + delete verifyAllVertexKleinBottlesInessential(tri, "RP3#RP3"); + + tri = Example<3>::smallClosedNonOrblHyperbolic(); + delete verifyAllVertexKleinBottlesInessential(tri, "Smallest known closed nonorientable hyperbolic"); + + p = 3; + q = 2; + while (p <= 34){ + tri = Example<3>::lst(p,q); + delete verifyAllVertexKleinBottlesInessential(tri, "Solid torus"); + if (p % 2 == 0){ + tri = Example<3>::lens(p,q); + ostringstream oss; + oss << "L(" << p << "," << q << ")" ; + delete verifyAllVertexKleinBottlesInessential(tri, oss.str()); + } + p = p + q; + q = p - q; + } + + tri = Example<3>::solidKleinBottle(); + delete verifyAllVertexKleinBottlesInessential(tri, "Solid Klein bottle"); + + tri = Example<3>::figureEight(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyAllVertexKleinBottlesInessential(tri, "Figure eight"); + + tri = Example<3>::trefoil(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyAllVertexKleinBottlesInessential(tri, "Trefoil"); + + tri = Example<3>::whiteheadLink(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyAllVertexKleinBottlesInessential(tri, "Whitehead link"); + + tri = Example<3>::gieseking(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyAllVertexKleinBottlesInessential(tri, "Gieseking manifold"); + + tri = Example<3>::cuspedGenusTwoTorus(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyAllVertexKleinBottlesInessential(tri, "Genus two surface x I"); + + // Manifolds with essential Klein bottles + + tri = new Triangulation<3>("gLALQbcceffflpkktua"); + delete verifyHasVertexEssentialKleinBottle(tri, "Doubled twisted I-bundle over K2"); + + tri = new Triangulation<3>("oLLLAAzLMQccefffggjlmnmmnnkkwawpwwjraakru"); + delete verifyHasVertexEssentialKleinBottle(tri, "Doubled Gieseking manifold"); + } void isEssentialTorus() { CPPUNIT_FAIL("Not implemented yet"); From 26c640a8e29415464a99a084aba4d0caddf76b82 Mon Sep 17 00:00:00 2001 From: "Robert C. Haraway, III" Date: Wed, 28 Oct 2020 12:18:44 -0500 Subject: [PATCH 23/31] Add essential Klein bottle method Added `isEssentialKleinBottle` function to `NormalSurface` class. This depends upon the `isTorusXInterval` function in the `Triangulation<3>` class. --- engine/surfaces/fault.cpp | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/engine/surfaces/fault.cpp b/engine/surfaces/fault.cpp index e95c5a2577..8663046fb1 100644 --- a/engine/surfaces/fault.cpp +++ b/engine/surfaces/fault.cpp @@ -49,15 +49,12 @@ namespace regina { && isCompact() && eulerChar() == 2)) return false; - - int tri_cpts = triangulation()->countComponents(); + + if (!separates()) + return true; + Triangulation<3>* cut_up = cutAlong(); cut_up->intelligentSimplify(); - int new_cpts = cut_up->countComponents(); - if (tri_cpts >= new_cpts){ - delete cut_up; - return true; - } // Cap sphere boundary components. cut_up->finiteToIdeal(); @@ -76,7 +73,30 @@ namespace regina { } bool NormalSurface::isEssentialKleinBottle() const{ - return false; + if (!(isConnected() + && isCompact() + && !isOrientable() + && !hasRealBoundary() + && eulerChar() == 0)) + return false; + + if (!isIncompressible()) + return false; + + if (!separates()) + return true; + + Triangulation<3>* cut_up = cutAlong(); + cut_up->splitIntoComponents(); + Triangulation<3>* L = (Triangulation<3>*) cut_up->firstChild(); + Triangulation<3>* R = (Triangulation<3>*) L->nextSibling(); + L->makeDoubleCover(); + R->makeDoubleCover(); + bool boundary_parallel = L->isTorusXInterval() || R->isTorusXInterval(); + delete L; + delete R; + delete cut_up; + return !boundary_parallel; } bool NormalSurface::isEssentialTorus() const{ From b05f2599b44cf6352b61744635df18099a422222 Mon Sep 17 00:00:00 2001 From: "Robert C. Haraway, III" Date: Wed, 28 Oct 2020 12:45:46 -0500 Subject: [PATCH 24/31] Add small SF spaces to Klein bottle test Add small Seifert fibered spaces to tests for `isEssentialKleinBottle`. --- testsuite/surfaces/fault.cpp | 34 ++++++++++++---------------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/testsuite/surfaces/fault.cpp b/testsuite/surfaces/fault.cpp index 039af05f6e..543b830086 100644 --- a/testsuite/surfaces/fault.cpp +++ b/testsuite/surfaces/fault.cpp @@ -454,12 +454,17 @@ class FaultTest : public CppUnit::TestFixture { int p = 3; int q = 2; while (p <= 34){ - if (p % 2 != 0){ - tri = Example<3>::lens(p,q); - ostringstream oss; - oss << "L(" << p << "," << q << ")" ; - delete verifyAllVertexKleinBottlesInessential(tri, oss.str()); - } + tri = Example<3>::lst(p,q); + delete verifyAllVertexKleinBottlesInessential(tri, "Solid torus"); + + tri = Example<3>::lens(p,q); + ostringstream oss; + oss << "L(" << p << "," << q << ")" ; + delete verifyAllVertexKleinBottlesInessential(tri, oss.str()); + + tri = smallSFS(2,1,2,1,p,-q); + delete verifyAllVertexKleinBottlesInessential(tri, oss.str() ); + p = p + q; q = p - q; } @@ -481,21 +486,6 @@ class FaultTest : public CppUnit::TestFixture { tri = Example<3>::smallClosedNonOrblHyperbolic(); delete verifyAllVertexKleinBottlesInessential(tri, "Smallest known closed nonorientable hyperbolic"); - - p = 3; - q = 2; - while (p <= 34){ - tri = Example<3>::lst(p,q); - delete verifyAllVertexKleinBottlesInessential(tri, "Solid torus"); - if (p % 2 == 0){ - tri = Example<3>::lens(p,q); - ostringstream oss; - oss << "L(" << p << "," << q << ")" ; - delete verifyAllVertexKleinBottlesInessential(tri, oss.str()); - } - p = p + q; - q = p - q; - } tri = Example<3>::solidKleinBottle(); delete verifyAllVertexKleinBottlesInessential(tri, "Solid Klein bottle"); @@ -533,7 +523,7 @@ class FaultTest : public CppUnit::TestFixture { tri = new Triangulation<3>("oLLLAAzLMQccefffggjlmnmmnnkkwawpwwjraakru"); delete verifyHasVertexEssentialKleinBottle(tri, "Doubled Gieseking manifold"); } - + void isEssentialTorus() { CPPUNIT_FAIL("Not implemented yet"); } From c46ac3d1f8f4362b6a8757bcf14001b5ec006775 Mon Sep 17 00:00:00 2001 From: "Robert C. Haraway, III" Date: Wed, 28 Oct 2020 13:04:35 -0500 Subject: [PATCH 25/31] Add essential torus test Add `isEssentialTorus` member function to `NormalSurface` class. Add unit tests for `isEssentialTorus`. --- engine/surfaces/fault.cpp | 23 ++++- testsuite/surfaces/fault.cpp | 174 ++++++++++++++++++++++++++++++++++- 2 files changed, 195 insertions(+), 2 deletions(-) diff --git a/engine/surfaces/fault.cpp b/engine/surfaces/fault.cpp index 8663046fb1..c83aff92f7 100644 --- a/engine/surfaces/fault.cpp +++ b/engine/surfaces/fault.cpp @@ -100,7 +100,28 @@ namespace regina { } bool NormalSurface::isEssentialTorus() const{ - return false; + if (!(isConnected() + && isCompact() + && isOrientable() + && !hasRealBoundary() + && eulerChar() == 0)) + return false; + + if (!isIncompressible()) + return false; + + if (!separates()) + return true; + + Triangulation<3>* cut_up = cutAlong(); + cut_up->splitIntoComponents(); + Triangulation<3>* L = (Triangulation<3>*) cut_up->firstChild(); + Triangulation<3>* R = (Triangulation<3>*) L->nextSibling(); + bool boundary_parallel = L->isTorusXInterval() || R->isTorusXInterval(); + delete L; + delete R; + delete cut_up; + return !boundary_parallel; } bool NormalSurface::isSolidTorusAnnulus() const{ diff --git a/testsuite/surfaces/fault.cpp b/testsuite/surfaces/fault.cpp index 543b830086..a46e70867b 100644 --- a/testsuite/surfaces/fault.cpp +++ b/testsuite/surfaces/fault.cpp @@ -524,8 +524,180 @@ class FaultTest : public CppUnit::TestFixture { delete verifyHasVertexEssentialKleinBottle(tri, "Doubled Gieseking manifold"); } + Triangulation<3>* verifyAllVertexToriInessential(Triangulation<3>* tri, + const std::string& triName){ + NormalSurfaces* s = NormalSurfaces::enumerate( + tri, NS_QUAD, NS_VERTEX_EMBEDDED); + bool found_essential_torus = false; + for (unsigned i = 0; i < s->size(); ++i) + if (s->surface(i)->isEssentialTorus()){ + found_essential_torus = true; + break; + } + delete s; + if (found_essential_torus) + CPPUNIT_FAIL(("The atoroidal manifold " + triName + + " was computed to have an essential torus.").c_str()); + return tri; + } + + Triangulation<3>* verifyHasVertexEssentialTorus(Triangulation<3>* tri, + const std::string& triName){ + NormalSurfaces* s = NormalSurfaces::enumerate( + tri, NS_QUAD, NS_VERTEX_EMBEDDED); + bool found_essential_torus = false; + for (unsigned i = 0; i < s->size(); ++i) + if (s->surface(i)->isEssentialTorus()){ + found_essential_torus = true; + break; + } + delete s; + if (!found_essential_torus) + CPPUNIT_FAIL(("No essential tori were found in the toroidal manifold " + + triName + ".").c_str()); + return tri; + } + void isEssentialTorus() { - CPPUNIT_FAIL("Not implemented yet"); + Triangulation<3>* tri; + + // Simple closed atoroidal manifolds + tri = Example<3>::threeSphere(); + delete verifyAllVertexToriInessential(tri, "Minimal 3-sphere"); + tri = Example<3>::simplicialSphere(); + delete verifyAllVertexToriInessential(tri, "Pentachoron boundary 3-sphere"); + tri = Example<3>::poincareHomologySphere(); + delete verifyAllVertexToriInessential(tri, "Poincare homology sphere"); + tri = Example<3>::weeks(); + delete verifyAllVertexToriInessential(tri, "Weeks-Matveev-Fomenko manifold"); + int p = 3; + int q = 2; + while (p < 1000){ + tri = Example<3>::lens(p,q); + delete verifyAllVertexToriInessential(tri, "Fibonacci lens space"); + tri = smallSFS(2,1,2,1,p,-q); + ostringstream oss; + oss << "SFS [S2: (2,1)(2,1)(" << p << "," << -q << ")]"; + delete verifyAllVertexToriInessential(tri, oss.str()); + p = p + q; + q = p - q; + } + + // Small Seifert fibered spaces... +#define VERIFY_SMALL_SFS_ATOROIDAL(a0,b0,a1,b1,a2,b2) \ + tri = smallSFS(a0,b0,a1,b1,a2,b2); \ + tri->intelligentSimplify(); \ + delete verifyAllVertexToriInessential(tri, "SFS [S2: (a0,b0) (a1,b1) (a2,b2)]") + + // ... of positive orbifold Euler characteristic ... + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,1,3,-2); + + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,1,3,-1); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,1,4,-3); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,1,5,-4); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,2,3,-1); + + /* + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,1,3,1); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,1,3,2); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,1,4,-1); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,1,5,-3); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,1,5,-2); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,2,3,2); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,2,4,-3); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,2,4,-1); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,2,5,-3); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,2,5,-2); + */ + + // ...and of negative orbifold Euler characteristic.... + /* + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,1,7,-6); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,1,7,-5); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,1,7,-5); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,1,7,-4); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,1,7,-2); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,1,8,-5); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,1,8,-3); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,2,7,-5); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,2,7,-4); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,2,7,-3); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,2,7,-2); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,2,8,-5); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,2,8,-3); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,3,2,8,-3); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,4,1,5,-4); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,4,1,5,-3); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,4,1,5,-2); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,4,3,5,-3); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,4,3,5,-2); + */ + VERIFY_SMALL_SFS_ATOROIDAL(2,1,5,2,5,-3); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,5,2,5,-2); + VERIFY_SMALL_SFS_ATOROIDAL(2,1,5,3,5,-2); + + VERIFY_SMALL_SFS_ATOROIDAL(3,1,3,1,4,-3); + VERIFY_SMALL_SFS_ATOROIDAL(3,1,3,1,4,-1); + VERIFY_SMALL_SFS_ATOROIDAL(3,1,3,1,5,-3); + VERIFY_SMALL_SFS_ATOROIDAL(3,1,3,1,5,-2); + /* + VERIFY_SMALL_SFS_ATOROIDAL(3,1,3,2,4,-3); + VERIFY_SMALL_SFS_ATOROIDAL(3,1,3,2,4,-1); + VERIFY_SMALL_SFS_ATOROIDAL(3,1,3,2,5,-3); + VERIFY_SMALL_SFS_ATOROIDAL(3,1,3,2,5,-2); + VERIFY_SMALL_SFS_ATOROIDAL(3,2,3,2,4,-3); + VERIFY_SMALL_SFS_ATOROIDAL(3,2,3,2,4,-1); + VERIFY_SMALL_SFS_ATOROIDAL(3,2,3,2,5,-3); + VERIFY_SMALL_SFS_ATOROIDAL(3,2,3,2,5,-2); + */ + + // Bounded atoroidal manifolds + tri = Example<3>::ball(); + delete verifyAllVertexToriInessential(tri, "One tetrahedron ball"); + tri = Example<3>::cuspedGenusTwoTorus(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyAllVertexToriInessential( + tri, "Trivial I-bundle over genus two surface"); + tri = Example<3>::figureEight(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyAllVertexToriInessential(tri, "Figure eight knot exterior"); + tri = Example<3>::trefoil(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyAllVertexToriInessential(tri, "Trefoil knot exterior"); + tri = Example<3>::whiteheadLink(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyAllVertexToriInessential(tri, "Whitehead link exterior"); + tri = Example<3>::gieseking(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyAllVertexToriInessential(tri, "Gieseking manifold"); + tri = Example<3>::solidKleinBottle(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyAllVertexToriInessential(tri, "Solid Klein bottle"); + p = 3; + q = 2; + while (p < 1000){ + tri = Example<3>::lst(p,q); + delete verifyAllVertexToriInessential(tri, "Solid torus"); + p = p + q; + q = p - q; + } + + // Toroidal manifolds + tri = new Triangulation<3>("uLLvPAPAzzvQPQccdeghiihjjlmqspstrstthshgbvrndhakecbcqvndm"); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyHasVertexEssentialTorus(tri, "Doubled figure eight knot exterior"); + + tri = new Triangulation<3>("iLALzQcbccefhgghlpkkucjjs"); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyHasVertexEssentialTorus(tri, "Doubled trefoil knot exterior"); } void isSolidTorusAnnulus() { From 1018fe14bf67a6afed62b5d30c7339d2651d1eb8 Mon Sep 17 00:00:00 2001 From: "Robert C. Haraway, III" Date: Thu, 29 Oct 2020 10:37:14 -0500 Subject: [PATCH 26/31] Added preconditions for fault tests to documentation `isEssentialSphere` is only correct for connected triangulations with no sphere boundary components. `isEssentialKleinBottle` and `isEssentialTorus` are only correct for connected irreducible boundary-incompressible triangulations. --- engine/surfaces/normalsurface.h | 36 +++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/engine/surfaces/normalsurface.h b/engine/surfaces/normalsurface.h index 77d9f14a3e..58ca0caafd 100644 --- a/engine/surfaces/normalsurface.h +++ b/engine/surfaces/normalsurface.h @@ -1449,16 +1449,48 @@ class REGINA_API NormalSurface : public ShortOutput { */ bool separates() const; + /** + * Determines whether or not this surface is an essential sphere. + * + * \pre The underlying triangulation is connected + * and has no sphere boundary components. + * \pre This normal surface can be cut along. + * + * @return \c true if this is an essential sphere, otherwise \c false. + */ bool isEssentialSphere() const; + /** + * Determines whether or not this surface is an essential Klein bottle. + * + * \pre The underlying triangulation is connected and irreducible, + * and has no compressing disc (i.e. is boundary-incompressible). + * \pre This normal surface can be cut along. + * + * @return \c true if this is an essential Klein bottle, else \c false. + */ bool isEssentialKleinBottle() const; - + + /** + * Determines whether or not this surface is an essential torus. + * + * \pre The underlying triangulation is connected and irreducible, + * and has no compressing disc (i.e. is boundary-incompressible). + * \pre This normal surface can be cut along. + * + * @return \c true if this is an essential torus, else \c false. + */ bool isEssentialTorus() const; /** * Determines whether or not this surface is an annulus - * cutting along which yields two solid tori. + * cutting along which yields a disjoint union of at most two solid tori, + * which we call a \e solid \e torus \e annulus. * This is easier to check than whether or not the annulus is essential. + * + * \pre This normal surface can be cut along. + * + * @return \c true if this surface is a solid torus annulus, else \c false. */ bool isSolidTorusAnnulus() const; From d09c673d9db68124a7454196310a17fc42f76aa2 Mon Sep 17 00:00:00 2001 From: "Robert C. Haraway, III" Date: Thu, 29 Oct 2020 14:17:14 -0500 Subject: [PATCH 27/31] Fix nonseparating compressibility error Make `isEssentialKleinBottle` and `isEssentialTorus` return \c false given a nonseparating compressible K2 (resp. T2). --- engine/surfaces/fault.cpp | 72 +++++++++++++++++++++++++++--------- testsuite/surfaces/fault.cpp | 20 +++++++--- 2 files changed, 69 insertions(+), 23 deletions(-) diff --git a/engine/surfaces/fault.cpp b/engine/surfaces/fault.cpp index c83aff92f7..036ab06051 100644 --- a/engine/surfaces/fault.cpp +++ b/engine/surfaces/fault.cpp @@ -52,24 +52,30 @@ namespace regina { if (!separates()) return true; - + Triangulation<3>* cut_up = cutAlong(); cut_up->intelligentSimplify(); // Cap sphere boundary components. + // Since the original triangulation has no sphere boundary components, + // this caps the sides of this sphere. + // That is, this undoes a (possibly trivial) connect-sum. cut_up->finiteToIdeal(); cut_up->intelligentSimplify(); cut_up->idealToFinite(); cut_up->intelligentSimplify(); + // There are two components, + // since the original triangulation was connected, + // and this surface is separating. cut_up->splitIntoComponents(); Triangulation<3>* L = (Triangulation<3>*) cut_up->firstChild(); Triangulation<3>* R = (Triangulation<3>*) L->nextSibling(); - bool essential = !(L->isThreeSphere() || R->isThreeSphere()); + bool bounds_ball = L->isThreeSphere() || R->isThreeSphere(); delete L; delete R; delete cut_up; - return essential; + return !bounds_ball; } bool NormalSurface::isEssentialKleinBottle() const{ @@ -77,26 +83,44 @@ namespace regina { && isCompact() && !isOrientable() && !hasRealBoundary() - && eulerChar() == 0)) + && eulerChar() == 0)){ return false; - + } + + if (!separates()){ + Triangulation<3>* cut_up = cutAlong(); + bool compressible = cut_up->hasCompressingDisc(); + delete cut_up; + return !compressible; + } + + /* if (!isIncompressible()) return false; - - if (!separates()) - return true; - + */ + // is what we would like to write here, + // but presently `isIncompressible` requires closed triangulations. + + // There are two components, + // since the original triangulation was connected, + // and this surface is separating. Triangulation<3>* cut_up = cutAlong(); cut_up->splitIntoComponents(); Triangulation<3>* L = (Triangulation<3>*) cut_up->firstChild(); Triangulation<3>* R = (Triangulation<3>*) L->nextSibling(); - L->makeDoubleCover(); - R->makeDoubleCover(); - bool boundary_parallel = L->isTorusXInterval() || R->isTorusXInterval(); + + bool compressible = true; + bool boundary_parallel = false; + compressible = L->hasCompressingDisc() || R->hasCompressingDisc(); + if (!compressible){ + L->makeDoubleCover(); + R->makeDoubleCover(); + boundary_parallel = L->isTorusXInterval() || R->isTorusXInterval(); + } delete L; delete R; delete cut_up; - return !boundary_parallel; + return !(compressible || boundary_parallel); } bool NormalSurface::isEssentialTorus() const{ @@ -107,21 +131,33 @@ namespace regina { && eulerChar() == 0)) return false; - if (!isIncompressible()) - return false; - if (!separates()) return true; + /* + if (!isIncompressible()) + return false; + */ + // is what we would like to write here, + // but presently `isIncompressible` requires closed triangulations. + // There are two components, + // since the original triangulation was connected, + // and this surface is separating. Triangulation<3>* cut_up = cutAlong(); cut_up->splitIntoComponents(); Triangulation<3>* L = (Triangulation<3>*) cut_up->firstChild(); Triangulation<3>* R = (Triangulation<3>*) L->nextSibling(); - bool boundary_parallel = L->isTorusXInterval() || R->isTorusXInterval(); + + bool compressible = true; + bool boundary_parallel = false; + compressible = L->hasCompressingDisc() || R->hasCompressingDisc(); + if (!compressible){ + boundary_parallel = L->isTorusXInterval() || R->isTorusXInterval(); + } delete L; delete R; delete cut_up; - return !boundary_parallel; + return !(compressible || boundary_parallel); } bool NormalSurface::isSolidTorusAnnulus() const{ diff --git a/testsuite/surfaces/fault.cpp b/testsuite/surfaces/fault.cpp index a46e70867b..608bb79059 100644 --- a/testsuite/surfaces/fault.cpp +++ b/testsuite/surfaces/fault.cpp @@ -44,6 +44,7 @@ using regina::Example; using regina::NormalListFlags; +using regina::NormalSurface; using regina::NormalSurfaces; using regina::NS_EMBEDDED_ONLY; using regina::NS_FUNDAMENTAL; @@ -385,14 +386,18 @@ class FaultTest : public CppUnit::TestFixture { Triangulation<3>* verifyAllVertexKleinBottlesInessential(Triangulation<3>* tri, const std::string& triName){ + Triangulation<3>* working = new Triangulation<3>(tri->isoSig()); NormalSurfaces* s = NormalSurfaces::enumerate( - tri, NS_QUAD, NS_VERTEX_EMBEDDED); + working, NS_QUAD, NS_VERTEX_EMBEDDED); bool found_essential_klein_bottle = false; - for (unsigned i = 0; i < s->size(); ++i) - if (s->surface(i)->isEssentialKleinBottle()){ + unsigned i = 0; + for (; i < s->size(); ++i){ + const NormalSurface* surf = s->surface(i); + if (surf->isEssentialKleinBottle()){ found_essential_klein_bottle = true; break; } + } delete s; if (found_essential_klein_bottle) CPPUNIT_FAIL((triName + @@ -453,18 +458,23 @@ class FaultTest : public CppUnit::TestFixture { int p = 3; int q = 2; + ostringstream oss; while (p <= 34){ tri = Example<3>::lst(p,q); delete verifyAllVertexKleinBottlesInessential(tri, "Solid torus"); tri = Example<3>::lens(p,q); - ostringstream oss; oss << "L(" << p << "," << q << ")" ; delete verifyAllVertexKleinBottlesInessential(tri, oss.str()); + oss.clear(); + oss.str(""); tri = smallSFS(2,1,2,1,p,-q); + oss << "SFS [S2, (2,1,2,1," << p << "," << q << ")]"; delete verifyAllVertexKleinBottlesInessential(tri, oss.str() ); - + oss.clear(); + oss.str(""); + p = p + q; q = p - q; } From dbe70403d761ab3c45002a579293c43a01b0708a Mon Sep 17 00:00:00 2001 From: "Robert C. Haraway, III" Date: Thu, 29 Oct 2020 14:36:59 -0500 Subject: [PATCH 28/31] Add unit tests for isSolidTorusAnnulus Add unit tests for the `isSolidTorusAnnulus` member function of the `NormalSurface` class. --- testsuite/surfaces/fault.cpp | 190 ++++++++++++++++++++++++++++++++++- 1 file changed, 188 insertions(+), 2 deletions(-) diff --git a/testsuite/surfaces/fault.cpp b/testsuite/surfaces/fault.cpp index 608bb79059..7866c27171 100644 --- a/testsuite/surfaces/fault.cpp +++ b/testsuite/surfaces/fault.cpp @@ -710,10 +710,196 @@ class FaultTest : public CppUnit::TestFixture { delete verifyHasVertexEssentialTorus(tri, "Doubled trefoil knot exterior"); } - void isSolidTorusAnnulus() { - CPPUNIT_FAIL("Not implemented yet"); + + Triangulation<3>* verifyNoVertexSolidTorusAnnulus(Triangulation<3>* tri, + const std::string& triName){ + NormalSurfaces* s = NormalSurfaces::enumerate( + tri, NS_QUAD, NS_VERTEX_EMBEDDED); + bool found_solid_torus_annulus = false; + for (unsigned i = 0; i < s->size(); ++i) + if (s->surface(i)->isSolidTorusAnnulus()){ + found_solid_torus_annulus = true; + break; + } + delete s; + if (found_solid_torus_annulus) + CPPUNIT_FAIL((triName + + " has a \"solid torus annulus.\"").c_str()); + return tri; + } + + Triangulation<3>* verifyHasVertexSolidTorusAnnulus(Triangulation<3>* tri, + const std::string& triName){ + NormalSurfaces* s = NormalSurfaces::enumerate( + tri, NS_QUAD, NS_VERTEX_EMBEDDED); + bool found_solid_torus_annulus = false; + for (unsigned i = 0; i < s->size(); ++i) + if (s->surface(i)->isSolidTorusAnnulus()){ + found_solid_torus_annulus = true; + break; + } + delete s; + if (!found_solid_torus_annulus) + CPPUNIT_FAIL(("No solid torus annulus found in " + + triName + ".").c_str()); + return tri; } + + void isSolidTorusAnnulus() { + Triangulation<3>* tri; + + // Closed manifolds + tri = Example<3>::threeSphere(); + delete verifyNoVertexSolidTorusAnnulus(tri, "Minimal 3-sphere"); + tri = Example<3>::simplicialSphere(); + delete verifyNoVertexSolidTorusAnnulus(tri, "Pentachoron boundary 3-sphere"); + tri = Example<3>::poincareHomologySphere(); + delete verifyNoVertexSolidTorusAnnulus(tri, "Poincare homology sphere"); + tri = Example<3>::weeks(); + delete verifyNoVertexSolidTorusAnnulus(tri, "Weeks-Matveev-Fomenko manifold"); + int p = 3; + int q = 2; + while (p < 1000){ + tri = Example<3>::lens(p,q); + delete verifyNoVertexSolidTorusAnnulus(tri, "Fibonacci lens space"); + tri = smallSFS(2,1,2,1,p,-q); + ostringstream oss; + oss << "SFS [S2: (2,1)(2,1)(" << p << "," << -q << ")]"; + delete verifyNoVertexSolidTorusAnnulus(tri, oss.str()); + p = p + q; + q = p - q; + } + + // Small Seifert fibered spaces... +#define VERIFY_SMALL_SFS_ANANNULAR(a0,b0,a1,b1,a2,b2) \ + tri = smallSFS(a0,b0,a1,b1,a2,b2); \ + tri->intelligentSimplify(); \ + delete verifyNoVertexSolidTorusAnnulus(tri, "SFS [S2: (a0,b0) (a1,b1) (a2,b2)]") + + // ... of positive orbifold Euler characteristic ... + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,1,3,-2); + + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,1,3,-1); + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,1,4,-3); + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,1,5,-4); + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,2,3,-1); + + /* + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,1,3,1); + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,1,3,2); + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,1,4,-1); + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,1,5,-3); + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,1,5,-2); + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,2,3,2); + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,2,4,-3); + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,2,4,-1); + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,2,5,-3); + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,2,5,-2); + */ + + // ...and of negative orbifold Euler characteristic.... + /* + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,1,7,-6); + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,1,7,-5); + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,1,7,-5); + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,1,7,-4); + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,1,7,-2); + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,1,8,-5); + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,1,8,-3); + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,2,7,-5); + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,2,7,-4); + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,2,7,-3); + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,2,7,-2); + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,2,8,-5); + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,2,8,-3); + VERIFY_SMALL_SFS_ANANNULAR(2,1,3,2,8,-3); + VERIFY_SMALL_SFS_ANANNULAR(2,1,4,1,5,-4); + VERIFY_SMALL_SFS_ANANNULAR(2,1,4,1,5,-3); + VERIFY_SMALL_SFS_ANANNULAR(2,1,4,1,5,-2); + VERIFY_SMALL_SFS_ANANNULAR(2,1,4,3,5,-3); + VERIFY_SMALL_SFS_ANANNULAR(2,1,4,3,5,-2); + */ + VERIFY_SMALL_SFS_ANANNULAR(2,1,5,2,5,-3); + VERIFY_SMALL_SFS_ANANNULAR(2,1,5,2,5,-2); + VERIFY_SMALL_SFS_ANANNULAR(2,1,5,3,5,-2); + + VERIFY_SMALL_SFS_ANANNULAR(3,1,3,1,4,-3); + VERIFY_SMALL_SFS_ANANNULAR(3,1,3,1,4,-1); + VERIFY_SMALL_SFS_ANANNULAR(3,1,3,1,5,-3); + VERIFY_SMALL_SFS_ANANNULAR(3,1,3,1,5,-2); + /* + VERIFY_SMALL_SFS_ANANNULAR(3,1,3,2,4,-3); + VERIFY_SMALL_SFS_ANANNULAR(3,1,3,2,4,-1); + VERIFY_SMALL_SFS_ANANNULAR(3,1,3,2,5,-3); + VERIFY_SMALL_SFS_ANANNULAR(3,1,3,2,5,-2); + VERIFY_SMALL_SFS_ANANNULAR(3,2,3,2,4,-3); + VERIFY_SMALL_SFS_ANANNULAR(3,2,3,2,4,-1); + VERIFY_SMALL_SFS_ANANNULAR(3,2,3,2,5,-3); + VERIFY_SMALL_SFS_ANANNULAR(3,2,3,2,5,-2); + */ + + // Bounded manifolds + + tri = Example<3>::ball(); + delete verifyNoVertexSolidTorusAnnulus(tri, "One tetrahedron ball"); + + // The following is not anannular, + // but it does not decompose along an annulus into solid tori. + tri = Example<3>::cuspedGenusTwoTorus(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyNoVertexSolidTorusAnnulus( + tri, "Trivial I-bundle over genus two surface"); + + tri = Example<3>::figureEight(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyNoVertexSolidTorusAnnulus(tri, "Figure eight knot exterior"); + + tri = Example<3>::whiteheadLink(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyNoVertexSolidTorusAnnulus(tri, "Whitehead link exterior"); + tri = Example<3>::gieseking(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyNoVertexSolidTorusAnnulus(tri, "Gieseking manifold"); + + tri = Example<3>::solidKleinBottle(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyNoVertexSolidTorusAnnulus(tri, "Solid Klein bottle"); + + p = 3; + q = 2; + while (p < 1000){ + tri = Example<3>::lst(p,q); + delete verifyNoVertexSolidTorusAnnulus(tri, "Solid torus"); + p = p + q; + q = p - q; + } + // Manifolds with solid torus annuli + + // Although the trefoil knot does admit a solid torus annulus, + // this annulus double covers a Mobius band. + // So it is possible for there not to be a vertex such annulus. + // Even so, the following triangulation does have such an annulus. + /* + tri = Example<3>::trefoil(); + tri->idealToFinite(); + tri->intelligentSimplify(); + */ + // For consistency we remove the call to intelligentSimplify. + tri = new Triangulation<3>("fLHPccdeeeqcieh"); + delete verifyHasVertexSolidTorusAnnulus(tri, "Trefoil knot exterior"); + + tri = new Triangulation<3>("gLKjMcacdeffjkxnqs"); + delete verifyHasVertexSolidTorusAnnulus(tri, "SFS D: (3,1) (5,-2)"); + + tri = new Triangulation<3>("mHLyMzMzMcbcdefghijklldwuxhqxhqxhw"); + delete verifyHasVertexSolidTorusAnnulus(tri, "SFS D: (2,1) (144,-89)"); + } }; void addFaultFinding(CppUnit::TextUi::TestRunner& runner) { From 893046a431e7de6f5ff1b78b3c6f51b9126778fa Mon Sep 17 00:00:00 2001 From: "Robert C. Haraway, III" Date: Thu, 29 Oct 2020 14:57:00 -0500 Subject: [PATCH 29/31] Add fault tests Add `separates`, `isEssentialSphere`, `isEssentialKleinBottle`, `isEssentialTorus`, and `isSolidTorusAnnulus` member functions to `NormalSurface` class, with unit tests, Python bindings, and documentation. --- engine/surfaces/fault.cpp | 24 +++++++++++++++++++++++- testsuite/surfaces/fault.cpp | 18 ++++++++++-------- 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/engine/surfaces/fault.cpp b/engine/surfaces/fault.cpp index 036ab06051..0facbbcccb 100644 --- a/engine/surfaces/fault.cpp +++ b/engine/surfaces/fault.cpp @@ -161,7 +161,29 @@ namespace regina { } bool NormalSurface::isSolidTorusAnnulus() const{ - return false; + if (!(isConnected() + && isCompact() + && isOrientable() + && hasRealBoundary() + && eulerChar() == 0)) + return false; + + Triangulation<3>* cut_up = cutAlong(); + cut_up->intelligentSimplify(); + + bool solid_torus; + if (cut_up->isConnected()) + solid_torus = cut_up->isSolidTorus(); + else { + cut_up->splitIntoComponents(); + Triangulation<3>* L = (Triangulation<3>*)(cut_up->firstChild()); + Triangulation<3>* R = (Triangulation<3>*)(L->nextSibling()); + solid_torus = L->isSolidTorus() && R->isSolidTorus(); + delete L; + delete R; + } + delete cut_up; + return solid_torus; } } diff --git a/testsuite/surfaces/fault.cpp b/testsuite/surfaces/fault.cpp index 7866c27171..fdb802f0d9 100644 --- a/testsuite/surfaces/fault.cpp +++ b/testsuite/surfaces/fault.cpp @@ -865,22 +865,24 @@ class FaultTest : public CppUnit::TestFixture { tri->intelligentSimplify(); delete verifyNoVertexSolidTorusAnnulus(tri, "Gieseking manifold"); - tri = Example<3>::solidKleinBottle(); - tri->idealToFinite(); - tri->intelligentSimplify(); - delete verifyNoVertexSolidTorusAnnulus(tri, "Solid Klein bottle"); - + // Manifolds with solid torus annuli + p = 3; q = 2; while (p < 1000){ tri = Example<3>::lst(p,q); - delete verifyNoVertexSolidTorusAnnulus(tri, "Solid torus"); + ostringstream oss; + oss << "Layered solid torus (" << p << "," << q << ")"; + delete verifyHasVertexSolidTorusAnnulus(tri, oss.str()); p = p + q; q = p - q; } - - // Manifolds with solid torus annuli + tri = Example<3>::solidKleinBottle(); + tri->idealToFinite(); + tri->intelligentSimplify(); + delete verifyHasVertexSolidTorusAnnulus(tri, "Solid Klein bottle"); + // Although the trefoil knot does admit a solid torus annulus, // this annulus double covers a Mobius band. // So it is possible for there not to be a vertex such annulus. From ddccccc2ce75dbaa7e8769b1475753692d423f1c Mon Sep 17 00:00:00 2001 From: "Robert C. Haraway, III" Date: Fri, 30 Oct 2020 12:34:40 -0500 Subject: [PATCH 30/31] Add placeholders and Python bindings for hyperbolicity test Add trivial code for `isHyperbolicHaken` and `knowsHyperbolicHaken` member functions of `Triangulation<3>` class. Add Python bindings for these functions. --- engine/triangulation/dim3/decompose.cpp | 8 ++++++++ engine/triangulation/dim3/triangulation3.h | 12 ++++++++++++ python/dim3/triangulation3.cpp | 2 ++ 3 files changed, 22 insertions(+) diff --git a/engine/triangulation/dim3/decompose.cpp b/engine/triangulation/dim3/decompose.cpp index 1fd8087092..b8354d6780 100644 --- a/engine/triangulation/dim3/decompose.cpp +++ b/engine/triangulation/dim3/decompose.cpp @@ -1244,5 +1244,13 @@ bool Triangulation<3>::knowsHaken() const { return haken_.known(); } +bool Triangulation<3>::isHyperbolicHaken() const { + return false; +} + +bool Triangulation<3>::knowsHyperbolicHaken() const { + return false; +} + } // namespace regina diff --git a/engine/triangulation/dim3/triangulation3.h b/engine/triangulation/dim3/triangulation3.h index 24310be51f..7ae031ee0f 100644 --- a/engine/triangulation/dim3/triangulation3.h +++ b/engine/triangulation/dim3/triangulation3.h @@ -2229,6 +2229,18 @@ class REGINA_API Triangulation<3> : */ bool knowsHaken() const; + /** + * Determines whether or not this manifold satisfies the preconditions + * for Thurston's hyperbolization theorem. + */ + bool isHyperbolicHaken() const; + + /** + * Is it known whether or not this manifold satisfies the preconditions + * for Thurston's hyperbolization theorem? + */ + bool knowsHyperbolicHaken() const; + /** * Searches for a "simple" compressing disc inside this * triangulation. diff --git a/python/dim3/triangulation3.cpp b/python/dim3/triangulation3.cpp index 0e0e266d6f..e56eedcf22 100644 --- a/python/dim3/triangulation3.cpp +++ b/python/dim3/triangulation3.cpp @@ -306,6 +306,8 @@ void addTriangulation3(pybind11::module& m) { &Triangulation<3>::hasSimpleCompressingDisc) .def("isHaken", &Triangulation<3>::isHaken) .def("knowsHaken", &Triangulation<3>::knowsHaken) + .def("isHyperbolicHaken", &Triangulation<3>::isHyperbolicHaken) + .def("knowsHyperbolicHaken", &Triangulation<3>::knowsHyperbolicHaken) .def("niceTreeDecomposition", &Triangulation<3>::niceTreeDecomposition, pybind11::return_value_policy::reference_internal) .def("makeDoubleCover", &Triangulation<3>::makeDoubleCover) From 3f107d4a234560cf019701d9b52c7ffe0350e8e8 Mon Sep 17 00:00:00 2001 From: "Robert C. Haraway, III" Date: Fri, 30 Oct 2020 14:31:31 -0500 Subject: [PATCH 31/31] Revert "Add placeholders and Python bindings for hyperbolicity test" Apply changes instead to a new branch hyperbolicHaken. This reverts commit ddccccc2ce75dbaa7e8769b1475753692d423f1c. --- engine/triangulation/dim3/decompose.cpp | 8 -------- engine/triangulation/dim3/triangulation3.h | 12 ------------ python/dim3/triangulation3.cpp | 2 -- 3 files changed, 22 deletions(-) diff --git a/engine/triangulation/dim3/decompose.cpp b/engine/triangulation/dim3/decompose.cpp index b8354d6780..1fd8087092 100644 --- a/engine/triangulation/dim3/decompose.cpp +++ b/engine/triangulation/dim3/decompose.cpp @@ -1244,13 +1244,5 @@ bool Triangulation<3>::knowsHaken() const { return haken_.known(); } -bool Triangulation<3>::isHyperbolicHaken() const { - return false; -} - -bool Triangulation<3>::knowsHyperbolicHaken() const { - return false; -} - } // namespace regina diff --git a/engine/triangulation/dim3/triangulation3.h b/engine/triangulation/dim3/triangulation3.h index 7ae031ee0f..24310be51f 100644 --- a/engine/triangulation/dim3/triangulation3.h +++ b/engine/triangulation/dim3/triangulation3.h @@ -2229,18 +2229,6 @@ class REGINA_API Triangulation<3> : */ bool knowsHaken() const; - /** - * Determines whether or not this manifold satisfies the preconditions - * for Thurston's hyperbolization theorem. - */ - bool isHyperbolicHaken() const; - - /** - * Is it known whether or not this manifold satisfies the preconditions - * for Thurston's hyperbolization theorem? - */ - bool knowsHyperbolicHaken() const; - /** * Searches for a "simple" compressing disc inside this * triangulation. diff --git a/python/dim3/triangulation3.cpp b/python/dim3/triangulation3.cpp index e56eedcf22..0e0e266d6f 100644 --- a/python/dim3/triangulation3.cpp +++ b/python/dim3/triangulation3.cpp @@ -306,8 +306,6 @@ void addTriangulation3(pybind11::module& m) { &Triangulation<3>::hasSimpleCompressingDisc) .def("isHaken", &Triangulation<3>::isHaken) .def("knowsHaken", &Triangulation<3>::knowsHaken) - .def("isHyperbolicHaken", &Triangulation<3>::isHyperbolicHaken) - .def("knowsHyperbolicHaken", &Triangulation<3>::knowsHyperbolicHaken) .def("niceTreeDecomposition", &Triangulation<3>::niceTreeDecomposition, pybind11::return_value_policy::reference_internal) .def("makeDoubleCover", &Triangulation<3>::makeDoubleCover)