1 // xsplit.cc: implementation of class form_finder
2 //////////////////////////////////////////////////////////////////////////
3 //
4 // Copyright 1990-2012 John Cremona
5 // Marcus Mo (parallel code)
6 //
7 // This file is part of the eclib package.
8 //
9 // eclib is free software; you can redistribute it and/or modify it
10 // under the terms of the GNU General Public License as published by the
11 // Free Software Foundation; either version 2 of the License, or (at your
12 // option) any later version.
13 //
14 // eclib is distributed in the hope that it will be useful, but WITHOUT
15 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 // for more details.
18 //
19 // You should have received a copy of the GNU General Public License
20 // along with eclib; if not, write to the Free Software Foundation,
21 // Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 //
23 //////////////////////////////////////////////////////////////////////////
24
25 #include <unistd.h> // for unlink() (not needed on linux)
26
27 #define USE_SPARSE 1
28 #define ECLIB_INT_NUM_THREADS 8
29 #define ECLIB_RECURSION_DIM_LIMIT 5821
30 //#define ECLIB_MULTITHREAD_DEBUG
31 #include <eclib/logger.h>
32 #include <eclib/xsplit.h>
33 #include <eclib/smatrix_elim.h>
34 subspace sparse_combine(const subspace& s1, const subspace& s2);
35 mat sparse_restrict(const mat& m, const subspace& s);
36 smat restrict_mat(const smat& m, const subspace& s);
37
38 // CLASS FORM_FINDER (was called splitter)
39
form_finder(splitter_base * hh,int plus,int maxd,int mind,int dualflag,int bigmatsflag,int v)40 form_finder::form_finder(splitter_base* hh, int plus, int maxd, int mind, int dualflag, int bigmatsflag, int v)
41 :h(hh), plusflag(plus), dual(dualflag), bigmats(bigmatsflag), verbose(v),
42 gnfcount(0), maxdepth(maxd), mindepth(mind)
43 {
44 eclogger::setLevel( verbose );
45 denom1 = h->matden();
46 dimen = h->matdim();
47
48 // Create and initialise new data object as root node
49 // passing a constant pointer of current form_finder object
50 // to data class constructor
51 root = new ff_data( this );
52
53 // Set initial values
54 // form_finder class is a friend of ff_data class
55 // so may access private members
56 root -> subdim_ = dimen;
57
58 targetdim = 1;
59 if( !plusflag ) { // full conjmat not needed when plusflag is true
60 targetdim=2;
61 if( bigmats ) root -> conjmat_ = h -> s_opmat(-1,dual);
62 }
63 }
64
~form_finder(void)65 form_finder::~form_finder(void) {
66 // Decendants of root node will be recursively deleted
67 // if they have not already been deleted during find()
68 // All dynamically created objects (subspaces) held
69 // in each data node will also be deleted.
70 delete root;
71 }
72
73 // This is only used when bigmats==1 and we compute opmats on the entire ambient space:
make_opmat(long i,ff_data & data)74 void form_finder::make_opmat(long i, ff_data &data) {
75 data.the_opmat_ = h -> s_opmat(i,dual,verbose);
76 }
77
make_submat(ff_data & data)78 void form_finder::make_submat( ff_data &data ) {
79 // Cache current data node depth
80 long depth = data.depth_;
81
82 if( bigmats ) {
83 // fetch the_opmat from file, or compute
84 make_opmat(depth,data);
85
86 if( depth == 0 ) data.submat_ = data.the_opmat_;
87 else {
88 ECLOG(1) << "restricting the_opmat to subspace...";
89 data.submat_ = restrict_mat(data.the_opmat_,*(data.abs_space_));
90 ECLOG(1) << "done." << endl;
91 }
92
93 data.the_opmat_ = smat(0,0); // releases its space
94 }
95 else {
96 if( data.submat_.nrows() == 0 ) // else we have it already
97 {
98 if( depth == 0 )
99 data.submat_ = h -> s_opmat(depth,1,verbose);
100 else
101 {
102 //data.submat_ = h -> s_opmat_restricted(depth,*(data.abs_space_),1,verbose);
103 data.submat_ = make_nested_submat(depth,data);
104 }
105 }
106 }
107 }
108
109 /**
110 * make_nested_submat()
111 *
112 * Computes and returns the submat -- new nested version
113 */
114
make_nested_submat(long ip,ff_data & data)115 smat form_finder::make_nested_submat(long ip, ff_data &data)
116 {
117 long depth = data.depth_; // current depth
118 long subdim = data.subdim_; // current dimension
119 ff_data *d = &data; // Pointer to nodes
120 int i, j, level;
121
122 ECLOG(1) << "Computing operator of size " << subdim
123 << " at depth " << depth << "..." << flush;
124
125 // first we go up the chain, composing pivotal indices
126
127 vec jlist = iota((scalar)subdim);
128 smat b = d->rel_space_->bas();
129 level = depth;
130 while (level--)
131 {
132 ECLOG(2) << "["<<level<<"]" << flush;
133 jlist = d->rel_space_->pivs()[jlist];
134 d->parent_->child_ = d;
135 d = d->parent_;
136 if(level) b = mult_mod_p(d->rel_space_->bas(), b, MODULUS);
137 }
138
139 // now compute the matrix of images of the j'th generator for j in jlist
140 ECLOG(2) << " basis done..." << flush;
141 smat m = h -> s_opmat_cols(ip, jlist, 0);
142 ECLOG(2) << " sub-opmat done..." << flush;
143 m = mult_mod_p(m,b,MODULUS);
144 ECLOG(1) <<" opmat done."<<endl;
145 return m;
146 }
147
148
149 /**
150 * go_down()
151 *
152 * Initiates creation of new subspace; data stored in new
153 * data node. Data node passed as parameter will become the
154 * _parent_ of this new data node.
155 */
go_down(ff_data & data,long eig,int last)156 void form_finder::go_down(ff_data &data, long eig, int last) {
157 // Cache current depth
158 long depth = data.depth_;
159
160 // Locate required child w.r.t test eigenvalue
161 ff_data *child = data.child( eig );
162
163 // Set new depth
164 child -> depth_ = depth + 1;
165
166 SCALAR eig2 = eig*denom1;
167
168 ECLOG(1) << "Increasing depth to " << depth+1 << ", "
169 << "trying eig = " << eig << "..."
170 << "after scaling, eig = " << eig2 << "..." << endl;
171 ssubspace s(0);
172
173 vector<int> submat_dim = dim(data.submat_);
174 stringstream submat_dim_ss;
175 std::copy(submat_dim.begin(),submat_dim.end(),ostream_iterator<int>(submat_dim_ss," "));
176
177 ECLOG(1) << "Using sparse elimination (size = [ "
178 << submat_dim_ss.str() << "], density ="
179 << density(data.submat_) << ")..." << flush;
180 ECLOG(3) << "submat = " << data.submat_ << flush;
181
182 s = eigenspace(data.submat_,eig2); // the relative eigenspace
183
184 // Increment data usage counter for parent node
185 data.increaseSubmatUsage();
186
187 // Reset current submat if all children have used it
188 // Save space (will recompute when needed)
189 //if( ( depth == 0 )
190 // && ( dim(s) > 0 )
191 // && ( data.submat_.nrows() > 1000 )
192 // && ( data.submatUsage_ == data.numChildren_ ) ) {
193 // data.submat_ = smat(0,0);
194 //}
195
196 ECLOG(1) << "done (dim = " << dim(s) << ")"<<endl;
197 // ECLOG(1) << ", combining subspaces..." << flush;
198
199 child -> rel_space_ = new ssubspace(s);
200 // if( depth == 0 )
201 // child -> abs_space_ = new ssubspace(s);
202 // else
203 // child -> abs_space_ = new ssubspace(combine( *(data.abs_space_),s ));
204 // ECLOG(1) << "done." << endl;
205
206 depth++; // Local depth increment (does not effect data nodes)
207
208 child -> subdim_ = dim( *(child -> rel_space_) );
209
210 ECLOG(1) << "Eigenvalue " << eig
211 << " has multiplicity " << child -> subdim_ << endl;
212 if(child -> subdim_>0) {
213 ECLOG(0) << " eig " << eig
214 << " gives new subspace at depth " << depth
215 << " of dimension " << child -> subdim_ << endl;
216 }
217 }
218
go_up(ff_data & data)219 void form_finder::go_up( ff_data &data ) {
220 // Cache pointer to parent data node for access after current node is deleted
221 ff_data *parent = data.parent_;
222
223 #ifdef ECLIB_MULTITHREAD
224 // Lock parent node with scoped lock
225 boost::mutex::scoped_lock lock( parent -> go_up_lock_ );
226 #ifdef ECLIB_MULTITHREAD_DEBUG
227 ECLOG(1) << "in go_up for eig=" << data.eigenvalue_
228 << " depth=" << data.depth_
229 <<" status=" << data.status_ << std::endl;
230 #endif
231 #endif
232
233 // Erasing node via children array of parent which calls destructor
234 // of object (ff_data), using eigenvalue as key
235 parent -> childStatus( data.eigenvalue_, COMPLETE );
236 parent -> eraseChild( data.eigenvalue_ );
237
238 #ifdef ECLIB_MULTITHREAD
239 lock.unlock();
240
241 // Only last child to complete will execute the following (Detects if parent is root node)
242 if( parent -> complete() && parent -> parent_ != NULL ) go_up( *parent );
243 #endif
244 }
245
make_basis(ff_data & data)246 void form_finder::make_basis( ff_data &data ) {
247 // Cache data values
248 long depth = data.depth();
249 long subdim = data.subdim();
250 vector< long > eiglist = data.eiglist();
251
252 if( subdim != targetdim ) {
253 cout << "error in form_finder::make_basis with eiglist = ";
254 for(int i=0; i<depth; i++)
255 cout << eiglist[i] << ",";
256 cout << "\nfinal subspace has dimension " << subdim << endl;
257 cout << "aborting this branch!" << endl;
258 return;
259 }
260
261 if(plusflag) {
262 // must treat separately since we did not
263 // define abs_space[0] in order to save space
264 if(depth==0) {
265 data.bplus_ = vec(dimen);
266 data.bplus_[1] = 1;
267 }
268 else {
269 //data.bplus_ = getbasis1(data.abs_space_);
270 data.bplus_ = make_basis1(data);
271 }
272
273 return;
274 }
275
276 ssubspace* s;
277 if( bigmats ) {
278 s = data.abs_space_; // only used when depth>0
279 }
280 ssubspace *spm_rel, *spm_abs;
281 SCALAR eig = denom1;
282 // if(depth) eig*=denom(*s);
283 smat subconjmat; // only used when depth>0
284 if( bigmats ) {
285 subconjmat = (depth) ? restrict_mat(data.conjmat_, *s) : data.conjmat_;
286 // will only be a 2x2 in this case (genus 1 only!)
287 }
288 else {
289 //subconjmat = h->s_opmat_restricted(-1,*s,1,verbose);
290 subconjmat = make_nested_submat(-1,data);
291 }
292
293 // C++11 loop over two variables (similar to python)
294 // for( int b : { -1,+1 } ) { /* use b as -1 or +1 */ }
295 for(long signeig=+1; signeig>-2; signeig-=2) {
296 SCALAR seig;
297 seig = eig;
298
299 if(signeig<0) seig = -eig;
300
301 if(depth) {
302 spm_rel = new ssubspace(eigenspace(subconjmat,seig));
303 //spm_abs = new ssubspace(combine(*s,*spm_rel));
304 }
305 else {
306 spm_rel = new ssubspace(eigenspace(subconjmat,seig));
307 //spm_abs = spm_rel;
308 }
309
310 if(dim(*spm_rel)!=1) {
311 cout << "error in form_finder::makebasis; ";
312 cout << "\nfinal (";
313
314 if(signeig>0) cout << "+";
315 else cout << "-";
316
317 cout << ") subspace has dimension " << dim(*spm_rel) << endl;
318 cout << "aborting this branch!" << endl;
319 //delete spm_abs;
320 delete spm_rel;
321 return;
322 }
323
324 //vec v = getbasis1(spm_abs);
325 vec w = make_basis2(data, spm_rel->bas().as_mat().col(1));
326
327 if(signeig>0) data.bplus_ = w;
328 else data.bminus_ = w;
329
330 //delete spm_abs;
331 delete spm_rel;
332 }
333 }
334
make_basis2(ff_data & data,const vec & v)335 vec form_finder::make_basis2(ff_data &data, const vec& v)
336 {
337 ff_data *d = &data;
338 int level = data.depth_;
339 vec w = v;
340 while (level--)
341 {
342 w = mult_mod_p(d->rel_space_->bas(), w, MODULUS);
343 d = d->parent_;
344 }
345 return lift(w);
346 }
347
make_basis1(ff_data & data)348 vec form_finder::make_basis1(ff_data &data)
349 {
350 vec v(1); v.set(1,1);
351 return make_basis2(data, v);
352 }
353
getbasis1(const ssubspace * s)354 vec getbasis1(const ssubspace* s)
355 {
356 return lift(basis(*s).as_mat().col(1));
357 }
358
lift(const vec & v)359 vec lift(const vec& v)
360 {
361 #ifdef MODULAR
362 VEC b=v, bb;
363 if(lift(b,MODULUS,bb))
364 b = bb;
365 else
366 cout << "Unable to lift eigenvector from mod " << MODULUS << endl;
367 #else
368 makeprimitive(b);
369 #endif
370 #ifdef MULTI
371 scalar n=0; // dummy variable to gt the right type in next line
372 return b.shorten(n);
373 #else
374 return b;
375 #endif
376 }
377
recover(vector<vector<long>> eigs)378 void form_finder::recover(vector< vector<long> > eigs) {
379 for(unsigned int iform=0; iform<eigs.size(); iform++) {
380 if(verbose) {
381 cout << "Form number " << iform+1 << " with eigs ";
382
383 int n = eigs[iform].size();
384 if(n>10) n = 10;
385
386 copy(eigs[iform].begin(), eigs[iform].begin() + n,
387 ostream_iterator<long>(cout, " "));
388 cout << "..." << endl;
389 }
390
391 splitoff(eigs[iform]);
392 }
393 // Clears all nodes. This cannot be done automatically since we
394 // don't know how many eigs lists were to be splt off.
395 root -> eraseChildren();
396 }
397
splitoff(const vector<long> & eigs)398 void form_finder::splitoff(const vector<long>& eigs) {
399
400 // Always start at root node
401 ff_data *current = root;
402
403 // Temporary variables
404 long depth = current -> depth_;
405 long subdim = current -> subdim_;
406
407 if( verbose ) {
408 cout << "Entering form_finder, depth = " << depth
409 << ", dimension " << subdim << endl;
410 }
411
412 // Walk down nodes (if any already created) for common branches
413 while( current -> numChildren_ > 0
414 && current -> child(eigs[depth]) != NULL ) {
415
416 // Update current node pointer
417 current = current -> child(eigs[depth]);
418
419 // Update data
420 depth = current -> depth_;
421 subdim = current -> subdim_;
422 if (verbose) {
423 cout << "...increasing depth to " << depth
424 << ", dimension " << subdim << endl;
425 }
426 }
427
428 // Current node is new branch point
429 // We trim all sub-branches ...
430 current -> eraseChildren();
431
432 if( verbose ) {
433 cout << "restarting at depth = " << depth << ", "
434 << "dimension " << subdim << endl;
435 }
436
437 // ... and grow a new branch down to required depth.
438 while( (subdim > targetdim) && (depth < maxdepth) ) {
439 // Get number of possible eigenvalues
440 if( current -> numChildren_ <= 0 ) {
441 vector<long> t_eigs = h->eigrange(depth);
442 current -> setChildren( t_eigs );
443 }
444
445 // Create new child node
446 ff_data *child = new ff_data( this );
447
448 // Configure data node ancestry
449 current -> addChild( eigs[depth], *child );
450
451 // Create submat for current node
452 make_submat( *current );
453
454 // Proceed to go down
455 go_down(*current,eigs[depth],1);
456
457 // Update to new values
458 current = child;
459 depth = current -> depth_;
460 subdim = current -> subdim_;
461 }
462
463 // Creating newforms
464 make_basis(*current);
465 h->use(current->bplus_,current->bminus_,eigs);
466
467 return;
468 }
469
find()470 void form_finder::find() {
471 #ifdef ECLIB_MULTITHREAD
472 // Set number of threads to use either through default
473 // ECLIB_INT_NUM_THREADS macro defined above, or
474 // ECLIB_EXT_NUM_THREADS environment variable.
475 unsigned int eclib_num_threads = ECLIB_INT_NUM_THREADS;
476
477 stringstream s;
478 s << getenv("ECLIB_EXT_NUM_THREADS");
479 if( !s.str().empty() ) eclib_num_threads = atoi(s.str().c_str());
480
481 // Start job queue. We keep job queue local to ensure threads are
482 // not kept busy for longer than necessary.
483 pool.start( eclib_num_threads, verbose );
484 #endif
485
486 // Proceed in recursive find, passing a node through
487 find( *root );
488
489 #ifdef ECLIB_MULTITHREAD
490 // Join all threads in threadpool to wait for all jobs to finish
491 // Or detect when all branches of the tree has been traversed
492 pool.close();
493 #endif
494
495 // Clear all nodes. This should have been be done automatically but not all nodes are deleted when running in multithreaded mode.
496 root -> eraseChildren();
497
498 // Now compute all newforms only if recursion has finished
499 if(verbose>1) cout << "Now performing use() on all lists at once" << endl;
500 for( int nf = 0; nf < gnfcount; nf++ ) {
501 h-> use(gbplus[nf],gbminus[nf],gaplist[nf]);
502 }
503 }
504
find(ff_data & data)505 void form_finder::find( ff_data &data ) {
506 // Cache values of current data
507 long depth = data.depth();
508 long subdim = data.subdim();
509 vector< long > eiglist = data.eiglist();
510
511 vector<long> subeiglist(eiglist.begin(),eiglist.begin()+depth);
512
513 int dimold = h->dimoldpart(subeiglist);
514
515 stringstream subeiglist_ss;
516 std::copy(subeiglist.begin(),subeiglist.end(),ostream_iterator<long>(subeiglist_ss," "));
517
518 ECLOG(0) << "In formfinder, depth = " << depth
519 << ", aplist = [ " << subeiglist_ss.str() << "];\t"
520 << "dimsofar=" << subdim
521 << ", dimold=" << dimold
522 << ", dimnew=" << subdim-dimold << "\n";
523
524 if( dimold == subdim ) {
525 data.setStatus( ALL_OLD ); // Set status of current node
526 ECLOG(0) << "Abandoning a common eigenspace of dimension " << subdim
527 << " which is a sum of oldclasses." << endl;
528 return; // This branch of the recursion ends: all is old
529 }
530
531 if( ( subdim == targetdim ) && ( depth > mindepth ) ) {
532 data.setStatus( FOUND_NEW ); // Set status of current node
533 make_basis( data );
534 store(data.bplus_,data.bminus_,subeiglist);
535 return;
536 }
537
538 if( depth == maxdepth ) {
539 data.setStatus( MAX_DEPTH );
540 if(1) { // we want to see THIS message whatever the verbosity level!
541 cout << "\nFound a " << subdim << "D common eigenspace\n";
542 cout << "Abandoning, even though oldforms only make up ";
543 cout << dimold << "D of this." << endl;
544 }
545 return;
546 }
547
548 // Pass data node through to make_submat()
549 // NOTE originally called in go_down(), but relocated here since
550 // it only needs to be called once per node.
551 make_submat(data);
552
553 // The recursive part:
554 vector<long> t_eigs = h->eigrange(depth);
555 vector<long>::const_iterator apvar = t_eigs.begin();
556
557 stringstream t_eigs_ss;
558 std::copy(t_eigs.begin(),t_eigs.end(),ostream_iterator<long>(t_eigs_ss," "));
559
560 ECLOG(0) << "Testing eigenvalues [ " << t_eigs_ss.str()
561 << "] at level " << (depth+1) << endl;
562
563 // Set children counter
564 data.setChildren( t_eigs );
565
566 while( apvar != t_eigs.end() ) {
567 ECLOG(1) << "Going down with ap = " << (*apvar) <<endl;
568 long eig = *apvar++;
569
570 // Initiate new data node, passing a constant reference of the current
571 // form_finder object to the data class constructor
572 ff_data *child = new ff_data( this );
573
574 // Configure data node ancestry
575 data.addChild( eig, *child );
576
577 #ifdef ECLIB_MULTITHREAD
578 if( data.subdim_ > ECLIB_RECURSION_DIM_LIMIT ) {
579 // Post newly created child node to threadpool
580 pool.post< ff_data >( *child );
581 }
582 else {
583 // Parallel granularity control. Continue in serial.
584 go_down( data, eig, apvar==t_eigs.end() );
585 if( child -> subdim_ > 0 ) find( *child );
586 //if( child -> status_ != INTERNAL || child -> subdim_ == 0 ) go_up( *child );
587 }
588 #else
589 // Pass through current data node and new test eigenvalue to go_down()
590 go_down( data, eig, apvar==t_eigs.end() );
591
592 // We pass new child node to find()
593 if( child -> subdim_ > 0 ) find( *child );
594
595 go_up( *child );
596 #endif
597 }
598
599 #ifndef ECLIB_MULTITHREAD
600 ECLOG(0) << "Finished at level " << (depth+1) << endl;
601 #endif
602 }
603
store(vec bp,vec bm,vector<long> eigs)604 void form_finder::store(vec bp, vec bm, vector<long> eigs) {
605 #ifdef ECLIB_MULTITHREAD
606 // Lock function
607 boost::mutex::scoped_lock lock( store_lock );
608 #endif
609
610 // Store sub-bplus,bminus,eiglists in class level containers
611 gbplus.push_back(bp);
612 gbminus.push_back(bm);
613 gaplist.push_back(eigs);
614
615 // Increment global counter
616 gnfcount++;
617
618 // Inform about newform count
619 ECLOG(1) << "Current newform subtotal count at " << gnfcount << endl;
620 }
621
622 #if (METHOD==2)
sparse_combine(const subspace & s1,const subspace & s2)623 subspace sparse_combine(const subspace& s1, const subspace& s2)
624 {
625 // we assume s1, s2 are subspace mod DEFAULT_MODULUS
626 scalar d=denom(s1)*denom(s2);
627 const smat& sm1(basis(s1)), sm2(basis(s2));
628 const mat& b = (sm1*sm2).as_mat();
629 const vec& p = pivots(s1)[pivots(s2)];
630 return subspace(b,p,d);
631 // return COMBINE(s1,s2);
632 }
633
sparse_restrict(const mat & m,const subspace & s)634 mat sparse_restrict(const mat& m, const subspace& s)
635 {
636 if(dim(s)==m.nrows()) return m; // trivial special case, s is whole space
637 scalar dd = denom(s); // will be 1 if s is a mod-p subspace
638 mat b(basis(s));
639 smat sm(m), sb(b);
640 vec piv=pivots(s);
641 smat smr = sm.select_rows(piv);
642 smat ans = smr*sb;
643 int check=0;
644 if(check) {
645 smat left = sm*sb;
646 if(dd!=1) {cout<<"(dd="<<dd<<")"; left.mult_by_scalar_mod_p(dd);}
647 smat right = sb*ans;
648 int ok = eqmodp(left,right);
649 if (!ok)
650 {
651 cout<<"Warning from sparse_restrict: subspace not invariant!\n";
652 cout<<"Difference = \n"<<left-right<<endl;
653 }
654 }
655 check=0;
656 if(check) {
657 int ok = (ans.as_mat()==RESTRICT(m,s));
658 if(!ok)
659 {
660 cout<<"Error in sparse_restrict: sparse result differs from normal!"<<endl;
661 }
662 }
663 return ans.as_mat();
664 }
665
restrict_mat(const smat & m,const subspace & s)666 smat restrict_mat(const smat& m, const subspace& s)
667 {
668 if(dim(s)==m.nrows()) return m; // trivial special case, s is whole space
669 return mult_mod_p(m.select_rows(pivots(s)),smat(basis(s)),MODULUS);
670 }
671
672 #endif
673
674 // end of XSPLIT.CC
675