~ubuntu-branches/ubuntu/vivid/regina-normal/vivid

« back to all changes in this revision

Viewing changes to engine/census/hyperbolic.cpp

  • Committer: Package Import Robot
  • Author(s): Ben Burton
  • Date: 2014-08-29 17:37:46 UTC
  • mfrom: (19.1.1 sid)
  • Revision ID: package-import@ubuntu.com-20140829173746-igmqc9b67y366a7u
Tags: 4.96-1
New upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
 
 
2
/**************************************************************************
 
3
 *                                                                        *
 
4
 *  Regina - A Normal Surface Theory Calculator                           *
 
5
 *  Computational Engine                                                  *
 
6
 *                                                                        *
 
7
 *  Copyright (c) 1999-2014, Ben Burton                                   *
 
8
 *  For further details contact Ben Burton (bab@debian.org).              *
 
9
 *                                                                        *
 
10
 *  This program is free software; you can redistribute it and/or         *
 
11
 *  modify it under the terms of the GNU General Public License as        *
 
12
 *  published by the Free Software Foundation; either version 2 of the    *
 
13
 *  License, or (at your option) any later version.                       *
 
14
 *                                                                        *
 
15
 *  As an exception, when this program is distributed through (i) the     *
 
16
 *  App Store by Apple Inc.; (ii) the Mac App Store by Apple Inc.; or     *
 
17
 *  (iii) Google Play by Google Inc., then that store may impose any      *
 
18
 *  digital rights management, device limits and/or redistribution        *
 
19
 *  restrictions that are required by its terms of service.               *
 
20
 *                                                                        *
 
21
 *  This program is distributed in the hope that it will be useful, but   *
 
22
 *  WITHOUT ANY WARRANTY; without even the implied warranty of            *
 
23
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
 
24
 *  General Public License for more details.                              *
 
25
 *                                                                        *
 
26
 *  You should have received a copy of the GNU General Public             *
 
27
 *  License along with this program; if not, write to the Free            *
 
28
 *  Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,       *
 
29
 *  MA 02110-1301, USA.                                                   *
 
30
 *                                                                        *
 
31
 **************************************************************************/
 
32
 
 
33
/* end stub */
 
34
 
 
35
#include <sstream>
 
36
#include "census/ngluingpermsearcher.h"
 
37
#include "triangulation/nedge.h"
 
38
#include "triangulation/nfacepair.h"
 
39
#include "triangulation/ntriangulation.h"
 
40
#include "utilities/boostutils.h"
 
41
#include "utilities/memutils.h"
 
42
 
 
43
namespace regina {
 
44
 
 
45
const char NHyperbolicMinSearcher::ECLASS_TWISTED = 1;
 
46
const char NHyperbolicMinSearcher::ECLASS_LOWDEG = 2;
 
47
 
 
48
const char NHyperbolicMinSearcher::dataTag_ = 'h';
 
49
 
 
50
NHyperbolicMinSearcher::NHyperbolicMinSearcher(const NFacePairing* pairing,
 
51
        const NFacePairing::IsoList* autos, bool orientableOnly,
 
52
        UseGluingPerms use, void* useArgs) :
 
53
        NEulerSearcher(0, pairing, autos, orientableOnly,
 
54
            PURGE_NON_MINIMAL_HYP, use, useArgs) {
 
55
}
 
56
 
 
57
void NHyperbolicMinSearcher::runSearch(long maxDepth) {
 
58
    unsigned nTets = getNumberOfTetrahedra();
 
59
    if (maxDepth < 0) {
 
60
        // Larger than we will ever see (and in fact grossly so).
 
61
        maxDepth = nTets * 4 + 1;
 
62
    }
 
63
 
 
64
    if (! started) {
 
65
        // Search initialisation.
 
66
        started = true;
 
67
 
 
68
        // Do we in fact have no permutation at all to choose?
 
69
        if (maxDepth == 0 || pairing_->dest(0, 0).isBoundary(nTets)) {
 
70
            use_(this, useArgs_);
 
71
            use_(0, useArgs_);
 
72
            return;
 
73
        }
 
74
 
 
75
        orderElt = 0;
 
76
        orientation[0] = 1;
 
77
    }
 
78
 
 
79
    // Is it a partial search that has already finished?
 
80
    if (orderElt == orderSize) {
 
81
        if (isCanonical())
 
82
            use_(this, useArgs_);
 
83
        use_(0, useArgs_);
 
84
        return;
 
85
    }
 
86
 
 
87
    // ---------- Selecting the individual gluing permutations ----------
 
88
 
 
89
    int minOrder = orderElt;
 
90
    int maxOrder = orderElt + maxDepth;
 
91
 
 
92
    NTetFace face, adj;
 
93
    int mergeResult;
 
94
    while (orderElt >= minOrder) {
 
95
        face = order[orderElt];
 
96
        adj = (*pairing_)[face];
 
97
 
 
98
        // TODO (long-term): Check for cancellation.
 
99
 
 
100
        // Move to the next permutation.
 
101
 
 
102
        // Be sure to preserve the orientation of the permutation if necessary.
 
103
        if ((! orientableOnly_) || adj.facet == 0)
 
104
            permIndex(face)++;
 
105
        else
 
106
            permIndex(face) += 2;
 
107
 
 
108
        // Are we out of ideas for this face?
 
109
        if (permIndex(face) >= 6) {
 
110
            // Yep.  Head back down to the previous face.
 
111
            permIndex(face) = -1;
 
112
            permIndex(adj) = -1;
 
113
            orderElt--;
 
114
 
 
115
            // Pull apart vertex and edge links at the previous level.
 
116
            if (orderElt >= minOrder) {
 
117
                splitVertexClasses();
 
118
                splitEdgeClasses();
 
119
            }
 
120
 
 
121
            continue;
 
122
        }
 
123
 
 
124
        // We are sitting on a new permutation to try.
 
125
        permIndex(adj) = NPerm4::invS3[permIndex(face)];
 
126
 
 
127
        // Merge edge links and run corresponding tests.
 
128
        if (mergeEdgeClasses()) {
 
129
            // We created an invalid edge.
 
130
            splitEdgeClasses();
 
131
            continue;
 
132
        }
 
133
 
 
134
        // Merge vertex links and run corresponding tests.
 
135
        mergeResult = mergeVertexClasses();
 
136
        if (mergeResult & VLINK_BAD_EULER) {
 
137
            // Our vertex link can never obtain the correct
 
138
            // Euler characteristic.  Stop now.
 
139
            splitVertexClasses();
 
140
            splitEdgeClasses();
 
141
            continue;
 
142
        }
 
143
 
 
144
        // Fix the orientation if appropriate.
 
145
        if (adj.facet == 0 && orientableOnly_) {
 
146
            // It's the first time we've hit this tetrahedron.
 
147
            if ((permIndex(face) + (face.facet == 3 ? 0 : 1) +
 
148
                    (adj.facet == 3 ? 0 : 1)) % 2 == 0)
 
149
                orientation[adj.simp] = -orientation[face.simp];
 
150
            else
 
151
                orientation[adj.simp] = orientation[face.simp];
 
152
        }
 
153
 
 
154
        // Move on to the next face.
 
155
        orderElt++;
 
156
 
 
157
        // If we're at the end, try the solution and step back.
 
158
        if (orderElt == orderSize) {
 
159
            // We in fact have an entire triangulation.
 
160
            // Run through the automorphisms and check whether our
 
161
            // permutations are in canonical form.
 
162
            if (isCanonical())
 
163
                use_(this, useArgs_);
 
164
 
 
165
            // Back to the previous face.
 
166
            orderElt--;
 
167
 
 
168
            // Pull apart vertex and edge links at the previous level.
 
169
            if (orderElt >= minOrder) {
 
170
                splitVertexClasses();
 
171
                splitEdgeClasses();
 
172
            }
 
173
        } else {
 
174
            // Not a full triangulation; just one level deeper.
 
175
 
 
176
            // We've moved onto a new face.
 
177
            // Be sure to get the orientation right.
 
178
            face = order[orderElt];
 
179
            if (orientableOnly_ && pairing_->dest(face).facet > 0) {
 
180
                // permIndex(face) will be set to -1 or -2 as appropriate.
 
181
                adj = (*pairing_)[face];
 
182
                if (orientation[face.simp] == orientation[adj.simp])
 
183
                    permIndex(face) = 1;
 
184
                else
 
185
                    permIndex(face) = 0;
 
186
 
 
187
                if ((face.facet == 3 ? 0 : 1) + (adj.facet == 3 ? 0 : 1) == 1)
 
188
                    permIndex(face) = (permIndex(face) + 1) % 2;
 
189
 
 
190
                permIndex(face) -= 2;
 
191
            }
 
192
 
 
193
            if (orderElt == maxOrder) {
 
194
                // We haven't found an entire triangulation, but we've
 
195
                // gone as far as we need to.
 
196
                // Process it, then step back.
 
197
                use_(this, useArgs_);
 
198
 
 
199
                // Back to the previous face.
 
200
                permIndex(face) = -1;
 
201
                orderElt--;
 
202
 
 
203
                // Pull apart vertex and edge links at the previous level.
 
204
                if (orderElt >= minOrder) {
 
205
                    splitVertexClasses();
 
206
                    splitEdgeClasses();
 
207
                }
 
208
            }
 
209
        }
 
210
    }
 
211
 
 
212
    // And the search is over.
 
213
 
 
214
    // Some extra sanity checking.
 
215
    if (minOrder == 0) {
 
216
        // Our vertex classes had better be 4n standalone vertices.
 
217
        if (nVertexClasses != 4 * nTets)
 
218
            std::cerr << "ERROR: nVertexClasses == "
 
219
                << nVertexClasses << " at end of search!" << std::endl;
 
220
        for (int i = 0; i < static_cast<int>(nTets) * 4; i++) {
 
221
            if (vertexState[i].parent != -1)
 
222
                std::cerr << "ERROR: vertexState[" << i << "].parent == "
 
223
                    << vertexState[i].parent << " at end of search!"
 
224
                    << std::endl;
 
225
            if (vertexState[i].rank != 0)
 
226
                std::cerr << "ERROR: vertexState[" << i << "].rank == "
 
227
                    << vertexState[i].rank << " at end of search!" << std::endl;
 
228
            if (vertexState[i].bdry != 3)
 
229
                std::cerr << "ERROR: vertexState[" << i << "].bdry == "
 
230
                    << vertexState[i].bdry << " at end of search!" << std::endl;
 
231
            if (vertexState[i].euler != 2)
 
232
                std::cerr << "ERROR: vertexState[" << i << "].euler == "
 
233
                    << vertexState[i].euler << " at end of search!"
 
234
                    << std::endl;
 
235
            if (vertexState[i].hadEqualRank)
 
236
                std::cerr << "ERROR: vertexState[" << i << "].hadEqualRank == "
 
237
                    "true at end of search!" << std::endl;
 
238
            if (vertexState[i].bdryEdges != 3)
 
239
                std::cerr << "ERROR: vertexState[" << i << "].bdryEdges == "
 
240
                    << static_cast<int>(vertexState[i].bdryEdges)
 
241
                    << " at end of search!" << std::endl;
 
242
            if (vertexState[i].bdryNext[0] != i)
 
243
                std::cerr << "ERROR: vertexState[" << i << "].bdryNext[0] == "
 
244
                    << vertexState[i].bdryNext[0] << " at end of search!"
 
245
                    << std::endl;
 
246
            if (vertexState[i].bdryNext[1] != i)
 
247
                std::cerr << "ERROR: vertexState[" << i << "].bdryNext[1] == "
 
248
                    << vertexState[i].bdryNext[1] << " at end of search!"
 
249
                    << std::endl;
 
250
            if (vertexState[i].bdryTwist[0])
 
251
                std::cerr << "ERROR: vertexState[" << i << "].bdryTwist == "
 
252
                    "true at end of search!" << std::endl;
 
253
            if (vertexState[i].bdryTwist[1])
 
254
                std::cerr << "ERROR: vertexState[" << i << "].bdryTwist == "
 
255
                    "true at end of search!" << std::endl;
 
256
        }
 
257
        for (unsigned i = 0; i < nTets * 8; i++)
 
258
            if (vertexStateChanged[i] != VLINK_JOIN_INIT)
 
259
                std::cerr << "ERROR: vertexStateChanged[" << i << "] == "
 
260
                    << vertexStateChanged[i] << " at end of search!"
 
261
                    << std::endl;
 
262
 
 
263
        // And our edge classes had better be 6n standalone edges.
 
264
        if (nEdgeClasses != 6 * nTets)
 
265
            std::cerr << "ERROR: nEdgeClasses == "
 
266
                << nEdgeClasses << " at end of search!" << std::endl;
 
267
        for (unsigned i = 0; i < nTets * 6; i++) {
 
268
            if (edgeState[i].parent != -1)
 
269
                std::cerr << "ERROR: edgeState[" << i << "].parent == "
 
270
                    << edgeState[i].parent << " at end of search!"
 
271
                    << std::endl;
 
272
            if (edgeState[i].rank != 0)
 
273
                std::cerr << "ERROR: edgeState[" << i << "].rank == "
 
274
                    << edgeState[i].rank << " at end of search!" << std::endl;
 
275
            if (edgeState[i].size != 1)
 
276
                std::cerr << "ERROR: edgeState[" << i << "].size == "
 
277
                    << edgeState[i].size << " at end of search!" << std::endl;
 
278
            if (! edgeState[i].bounded)
 
279
                std::cerr << "ERROR: edgeState[" << i << "].bounded == "
 
280
                    "false at end of search!" << std::endl;
 
281
            if (edgeState[i].hadEqualRank)
 
282
                std::cerr << "ERROR: edgeState[" << i << "].hadEqualRank == "
 
283
                    "true at end of search!" << std::endl;
 
284
        }
 
285
        for (unsigned i = 0; i < nTets * 8; i++)
 
286
            if (edgeStateChanged[i] != -1)
 
287
                std::cerr << "ERROR: edgeStateChanged[" << i << "] == "
 
288
                    << edgeStateChanged[i] << " at end of search!"
 
289
                    << std::endl;
 
290
    }
 
291
 
 
292
    use_(0, useArgs_);
 
293
}
 
294
 
 
295
void NHyperbolicMinSearcher::dumpData(std::ostream& out) const {
 
296
    NEulerSearcher::dumpData(out);
 
297
}
 
298
 
 
299
NHyperbolicMinSearcher::NHyperbolicMinSearcher(std::istream& in,
 
300
        UseGluingPerms use, void* useArgs) :
 
301
        NEulerSearcher(in, use, useArgs) {
 
302
    if (inputError_)
 
303
        return;
 
304
 
 
305
    // Did we hit an unexpected EOF?
 
306
    if (in.eof())
 
307
        inputError_ = true;
 
308
}
 
309
 
 
310
int NHyperbolicMinSearcher::mergeEdgeClasses() {
 
311
    /**
 
312
     * As well as detecting edges that are self-identified in reverse,
 
313
     * we strip out low-degree edges here.  Although we are also interested
 
314
     * in non-geometric triangulations, we can still ignore triangulations
 
315
     * with low-degree edges because (with a little work) they can be
 
316
     * proven to be non-minimal.  For details see:
 
317
     * "The cusped hyperbolic census is complete", B.B.
 
318
     */
 
319
    NTetFace face = order[orderElt];
 
320
    NTetFace adj = (*pairing_)[face];
 
321
 
 
322
    int retVal = 0;
 
323
 
 
324
    NPerm4 p = gluingPerm(face);
 
325
    int v1, w1, v2, w2;
 
326
    int e, f;
 
327
    int orderIdx;
 
328
    int eRep, fRep;
 
329
    int middleTet;
 
330
 
 
331
    v1 = face.facet;
 
332
    w1 = p[v1];
 
333
 
 
334
    char parentTwists, hasTwist;
 
335
    for (v2 = 0; v2 < 4; v2++) {
 
336
        if (v2 == v1)
 
337
            continue;
 
338
 
 
339
        w2 = p[v2];
 
340
 
 
341
        // Look at the edge opposite v1-v2.
 
342
        e = 5 - NEdge::edgeNumber[v1][v2];
 
343
        f = 5 - NEdge::edgeNumber[w1][w2];
 
344
 
 
345
        orderIdx = v2 + 4 * orderElt;
 
346
 
 
347
        // We declare the natural orientation of an edge to be smaller
 
348
        // vertex to larger vertex.
 
349
        hasTwist = (p[NEdge::edgeVertex[e][0]] > p[NEdge::edgeVertex[e][1]] ?
 
350
            1 : 0);
 
351
 
 
352
        parentTwists = 0;
 
353
        eRep = findEdgeClass(e + 6 * face.simp, parentTwists);
 
354
        fRep = findEdgeClass(f + 6 * adj.simp, parentTwists);
 
355
 
 
356
        if (eRep == fRep) {
 
357
            edgeState[eRep].bounded = false;
 
358
 
 
359
            if (edgeState[eRep].size <= 2)
 
360
                retVal |= ECLASS_LOWDEG;
 
361
            else if (edgeState[eRep].size == 3) {
 
362
                // Flag as LOWDEG only if three distinct tetrahedra are used.
 
363
                middleTet = pairing_->dest(face.simp, v2).simp;
 
364
                if (face.simp != adj.simp && adj.simp != middleTet &&
 
365
                        middleTet != face.simp)
 
366
                    retVal |= ECLASS_LOWDEG;
 
367
            }
 
368
            if (hasTwist ^ parentTwists)
 
369
                retVal |= ECLASS_TWISTED;
 
370
 
 
371
            edgeStateChanged[orderIdx] = -1;
 
372
        } else {
 
373
            if (edgeState[eRep].rank < edgeState[fRep].rank) {
 
374
                // Join eRep beneath fRep.
 
375
                edgeState[eRep].parent = fRep;
 
376
                edgeState[eRep].twistUp = hasTwist ^ parentTwists;
 
377
                edgeState[fRep].size += edgeState[eRep].size;
 
378
 
 
379
                edgeStateChanged[orderIdx] = eRep;
 
380
            } else {
 
381
                // Join fRep beneath eRep.
 
382
                edgeState[fRep].parent = eRep;
 
383
                edgeState[fRep].twistUp = hasTwist ^ parentTwists;
 
384
                if (edgeState[eRep].rank == edgeState[fRep].rank) {
 
385
                    edgeState[eRep].rank++;
 
386
                    edgeState[fRep].hadEqualRank = true;
 
387
                }
 
388
                edgeState[eRep].size += edgeState[fRep].size;
 
389
 
 
390
                edgeStateChanged[orderIdx] = fRep;
 
391
            }
 
392
 
 
393
            nEdgeClasses--;
 
394
        }
 
395
    }
 
396
 
 
397
    return retVal;
 
398
}
 
399
 
 
400
void NHyperbolicMinSearcher::splitEdgeClasses() {
 
401
    NTetFace face = order[orderElt];
 
402
 
 
403
    int v1, v2;
 
404
    int e;
 
405
    int eIdx, orderIdx;
 
406
    int rep, subRep;
 
407
 
 
408
    v1 = face.facet;
 
409
 
 
410
    for (v2 = 3; v2 >= 0; v2--) {
 
411
        if (v2 == v1)
 
412
            continue;
 
413
 
 
414
        // Look at the edge opposite v1-v2.
 
415
        e = 5 - NEdge::edgeNumber[v1][v2];
 
416
 
 
417
        eIdx = e + 6 * face.simp;
 
418
        orderIdx = v2 + 4 * orderElt;
 
419
 
 
420
        if (edgeStateChanged[orderIdx] < 0)
 
421
            edgeState[findEdgeClass(eIdx)].bounded = true;
 
422
        else {
 
423
            subRep = edgeStateChanged[orderIdx];
 
424
            rep = edgeState[subRep].parent;
 
425
 
 
426
            edgeState[subRep].parent = -1;
 
427
            if (edgeState[subRep].hadEqualRank) {
 
428
                edgeState[subRep].hadEqualRank = false;
 
429
                edgeState[rep].rank--;
 
430
            }
 
431
 
 
432
            edgeState[rep].size -= edgeState[subRep].size;
 
433
 
 
434
            edgeStateChanged[orderIdx] = -1;
 
435
            nEdgeClasses++;
 
436
        }
 
437
    }
 
438
}
 
439
 
 
440
} // namespace regina
 
441