HepLib
Apart.cpp
Go to the documentation of this file.
1 
6 #include "HEP.h"
7 
8 namespace HepLib {
9 
10  #ifndef DOXYGEN_SKIP
11 
12  unsigned ApartIR1_SERIAL::serial = GiNaC::function::register_new(function_options("ApartIR",1).do_not_evalf_params().overloaded(2));
13  unsigned ApartIR2_SERIAL::serial = GiNaC::function::register_new(function_options("ApartIR",2).do_not_evalf_params().overloaded(2));
14 
15  #endif
16 
17  namespace {
18  inline bool isOK(const ex &expr_in, const lst &vars) {
19  ex item = expr_in;
20  if(is_a<power>(item)) {
21  if(!is_a<numeric>(item.op(1)) || !ex_to<numeric>(item.op(1)).is_integer()) return false;
22  item = item.op(0);
23  }
24  exmap vs; symbol s;
25  for(auto v : vars) vs[v] = s*v;
26  return item.subs(vs,nopat).degree(s)<=1;
27  }
28  }
29 
35  ex ApartIR2ex(const ex & expr_in) {
36  ex ret = expr_in;
37  ret = MapFunction([](const ex & e, MapFunction &self)->ex{
38  if(e.match(ApartIR(1)) || e.match(ApartIR(1,w))) return 1;
39  else if(!e.has(ApartIR(w1,w2))) return e;
40  else if(e.match(ApartIR(w1, w2))) {
41  ex vars = e.op(1);
42  ex ret=1;
43  matrix mat = ex_to<matrix>(e.op(0));
44  for(int c=0; c<mat.cols(); c++) {
45  ex sum=0;
46  for(int r=0; r<mat.rows()-2; r++) sum += mat(r,c) * vars.op(r);
47  sum += mat(mat.rows()-2,c);
48  ret *= pow(sum, mat(mat.rows()-1,c));
49  }
50  return ret;
51  } else return e.map(self);
52  })(ret);
53  return ret;
54  }
55 
61  ex ApartIR2F(const ex & expr_in) {
62  ex ret = expr_in;
63  ret = MapFunction([](const ex & e, MapFunction &self)->ex{
64  if(e.match(ApartIR(1)) || e.match(ApartIR(1,w))) return 1;
65  else if(!e.has(ApartIR(w1,w2))) return e;
66  else if(e.match(ApartIR(w1, w2))) {
67  ex vars = e.op(1);
68  matrix mat = ex_to<matrix>(e.op(0));
69  lst pns;
70  for(int c=0; c<mat.cols(); c++) {
71  ex sum=0;
72  for(int r=0; r<mat.rows()-2; r++) sum += mat(r,c) * vars.op(r);
73  sum += mat(mat.rows()-2,c);
74  if(is_zero(mat(mat.rows()-1,c))) continue;
75  pns.append(lst{ sum, ex(0)-mat(mat.rows()-1,c) });
76  }
77  sort_lst(pns); // need sort_lst
78  lst ps, ns;
79  for(auto item : pns) {
80  ps.append(item.op(0));
81  ns.append(item.op(1));
82  }
83  return F(ps, ns);
84  } else return e.map(self);
85  })(ret);
86  return ret;
87  }
88 
89  inline ex air2pn(ex air) {
90  matrix mat = ex_to<matrix>(air.op(0));
91  auto vars = air.op(1);
92  lst pns;
93  for(int c=0; c<mat.cols(); c++) {
94  ex sum=0;
95  for(int r=0; r<mat.rows()-2; r++) sum += mat(r,c) * vars.op(r);
96  sum += mat(mat.rows()-2,c);
97  if(is_zero(mat(mat.rows()-1,c))) continue;
98  pns.append(lst{ sum, mat(mat.rows()-1,c) });
99  }
100  sort_lst(pns); // need sort_lst
101  lst ps, ns;
102  for(auto item : pns) {
103  ps.append(item.op(0));
104  ns.append(item.op(1));
105  }
106  return lst{ps, ns};
107  }
108 
109  inline ex pn2mat(ex ps, ex ns, ex vars) { // no need to use normal and removed
110  lst pnlst;
111  for(int i=0; i<ps.nops(); i++) pnlst.append(lst{ ps.op(i), ns.op(i) });
112  sort_lst(pnlst); // need sort_lst
113  int nrow=vars.nops(), ncol=pnlst.nops();
114  exmap vars0;
115  for(auto v : vars) vars0[v]=0;
116  matrix mat(nrow+2, ncol);
117  for(int c=0; c<ncol; c++) {
118  ex pn = pnlst.op(c);
119  mat(nrow+1,c) = normal(pn.op(1));
120  auto tmp = pn.op(0);
121  for(int r=0; r<nrow; r++) {
122  mat(r,c) = (tmp.coeff(vars.op(r))); // remove exnormal
123  }
124  mat(nrow,c) = (tmp.subs(vars0,nopat)); // remove exnormal
125  }
126  return mat;
127  }
128 
129  // e1, e2 are sorted list, check if e1 is a subset of e2 or not.
130  inline bool is_subset(const ex & e1, const ex & e2) {
131  int i1=0, i2=0;
132  int t1 = e1.nops();
133  int t2 = e2.nops();
134  if(t1>t2) return false;
135  while(i1<t1) {
136  if(t1-i1>t2-i2) return false;
137  if(e1.op(i1).is_equal(e2.op(i2))) {
138  i1++; i2++; continue;
139  }
140  i2++;
141  }
142  return true;
143  }
144 
145  inline ex pn2p(ex pn) {
146  lst l;
147  auto p = pn.op(0);
148  auto n = pn.op(1);
149  for(int i=0; i<n.nops(); i++) {
150  if(n.op(i).is_zero()) continue;
151  l.append(p.op(i));
152  }
153  return l;
154  }
155 
156  exmap ApartRules(const exvector &airs, bool irc) { // irc=true to include ApartIRC
157  exmap rules;
158  if(airs.size()<1) return rules;
159  #define CHECK false
160  int nlimit = 50;
161  vector<exset> nps_set(airs[0].op(1).nops());
162  for(auto air : airs) {
163  auto pn = air2pn(air);
164  auto l = pn2p(pn);
165  nps_set[l.nops()-1].insert(l);
166  }
167  vector<exvector> nps(nps_set.size());
168  for(int i=0; i<nps_set.size(); i++) nps[i] = exvector(nps_set[i].begin(), nps_set[i].end());
169  nps_set.clear();
170 
171  exmap s2s;
172  for(int i=0; i<nps.size()-1; i++) {
173  auto psi = nps[i];
174  if(psi.size()<1) continue;
175  if(psi.size()<nlimit) {
176  for(auto si : psi) {
177  for(int j=nps.size()-1; j>i; j--) {
178  auto psj = nps[j];
179  for(auto sj : psj) {
180  if(is_subset(si,sj)) { s2s[si] = sj; goto nextsi; }
181  }
182  }
183  nextsi: ;
184  }
185  } else {
186  exvector psi_vec(psi.begin(), psi.end());
187  auto ret = GiNaC_Parallel(psi_vec.size(), [&psi_vec,&nps,i](int idx)->ex{
188  auto si = psi_vec[idx];
189  for(int j=nps.size()-1; j>i; j--) {
190  auto psj = nps[j];
191  for(int jj=0; jj<psj.size(); jj++) {
192  //if(is_subset(si,sj)) return lst{si, sj};
193  auto sj = psj[jj];
194  if(is_subset(si,sj)) return lst{idx, j, jj};
195  }
196  }
197  return lst{ };
198  }, "AR-"+to_string(nps.size()-1)+"-"+to_string(i+1));
199  for(auto item : ret) if(item.nops()>1) {
200  int idx = ex_to<numeric>(item.op(0)).to_int();
201  int j = ex_to<numeric>(item.op(1)).to_int();
202  int jj = ex_to<numeric>(item.op(2)).to_int();
203  s2s[psi_vec[idx]] = nps[j][jj];
204  }
205  }
206  }
207 
208  if(airs.size()<nlimit) {
209  for(int k=0; k<airs.size(); k++) {
210  auto pn = air2pn(airs[k]);
211  auto p = pn.op(0);
212  auto n = pn.op(1);
213  exmap p2n;
214  for(int i=0; i<p.nops(); i++) if(!is_zero(n.op(i))) p2n[p.op(i)] = n.op(i);
215  auto pk = pn2p(pn);
216  auto fi = s2s.find(pk);
217  if(fi!=s2s.end()) {
218  auto pp = fi->second;
219  lst nn;
220  for(int i=0; i<pp.nops(); i++) nn.append(p2n[pp.op(i)]);
221  ex air = ApartIR(pn2mat(pp,nn,airs[k].op(1)),airs[k].op(1));
222  if(CHECK) { // check
223  ex eL=1, eR=1;
224  for(int i=0; i<p.nops(); i++) if(!n.op(i).is_zero()) eL *= pow(WF(p.op(i)), n.op(i));
225  for(int i=0; i<pp.nops(); i++) if(!nn.op(i).is_zero()) eR *= pow(WF(pp.op(i)), nn.op(i));
226  ex chk = normal(eL-eR);
227  if(!chk.is_zero()) {
228  cout << p << ", " << n << endl;
229  cout << pp << ", " << nn << endl;
230  throw Error("ApartRules: check failed!");
231  }
232  }
233  if(irc) air = ApartIRC(air);
234  rules[airs[k]] = air;
235  } else if(irc) rules[airs[k]] = ApartIRC(airs[k]);
236  }
237  } else {
238  auto ret = GiNaC_Parallel(airs.size(), [&airs,&s2s,irc](int k)->ex{
239  auto pn = air2pn(airs[k]);
240  auto p = pn.op(0);
241  auto n = pn.op(1);
242  exmap p2n;
243  for(int i=0; i<p.nops(); i++) if(!is_zero(n.op(i))) p2n[p.op(i)] = n.op(i);
244  auto pk = pn2p(pn);
245  auto fi = s2s.find(pk);
246  if(fi!=s2s.end()) {
247  auto pp = fi->second;
248  lst nn;
249  for(int i=0; i<pp.nops(); i++) nn.append(p2n[pp.op(i)]);
250  ex air = ApartIR(pn2mat(pp,nn,airs[k].op(1)),airs[k].op(1));
251  air = ApartIRC(air);
252  if(CHECK) { // check
253  ex eL=1, eR=1;
254  for(int i=0; i<p.nops(); i++) if(!n.op(i).is_zero()) eL *= pow(WF(p.op(i)), n.op(i));
255  for(int i=0; i<pp.nops(); i++) if(!nn.op(i).is_zero()) eR *= pow(WF(pp.op(i)), nn.op(i));
256  ex chk = normal(eL-eR);
257  if(!chk.is_zero()) {
258  cout << p << ", " << n << endl;
259  cout << pp << ", " << nn << endl;
260  throw Error("ApartRules: check failed!");
261  }
262  }
263  if(irc) air = ApartIRC(air);
264  return lst{ airs[k], air };
265  } else if(irc) return lst{ airs[k], ApartIRC(airs[k]) };
266  else return lst{ };
267  }, "AR");
268  //ReShare(ret,airs);
269  for(auto lr : ret) if(lr.nops()>1) rules[lr.op(0)] = lr.op(1);
270  }
271  return rules;
272 
273  }
274 
280  ex Apart(const matrix & mat) {
281  static exmap mat_cache;
282  if(using_cache && cache_limit>0 && mat_cache.size() > cache_limit) mat_cache.clear();
283  auto mat_itr = mat_cache.find(mat);
284  if(mat_itr!=mat_cache.end()) return mat_itr->second;
285 
286  int nrow = mat.rows()-2;
287  int ncol = mat.cols();
288  lst null_vec;
289 
290  // null vector
291  static exmap null_cache;
292  if(cache_limit>0 && null_cache.size() > cache_limit) null_cache.clear();
293  ex key = sub_matrix(mat,0,nrow,0,ncol);
294  auto null_itr = null_cache.find(key);
295  if(null_itr==null_cache.end()) {
296  if(Apart_using_fermat) {
297  Fermat &fermat = Fermat::get();
298  int &v_max = fermat.vmax;
299 
300  lst rep_vs;
301  ex tree = mat;
302  for(const_preorder_iterator i = tree.preorder_begin(); i != tree.preorder_end(); ++i) {
303  auto e = (*i);
304  if(is_a<symbol>(e) || is_a<Pair>(e) || is_a<Eps>(e)) {
305  rep_vs.append(e);
306  }
307  }
308  rep_vs.sort();
309  rep_vs.unique();
310 
311  exmap v2f;
312  symtab st;
313  int fvi = 0;
314  for(auto vi : rep_vs) {
315  auto name = "v" + to_string(fvi);
316  v2f[vi] = Symbol(name);
317  st[name] = vi;
318  fvi++;
319  }
320 
321  stringstream ss;
322  if(fvi>111) {
323  cout << rep_vs << endl;
324  throw Error("Fermat: Too many variables.");
325  }
326  if(fvi>v_max) {
327  for(int i=v_max; i<fvi; i++) ss << "&(J=v" << i << ");" << endl;
328  fermat.Execute(ss.str());
329  ss.clear();
330  ss.str("");
331  v_max = fvi;
332  }
333 
334  ss << "Array m[" << nrow << "," << ncol+1 << "];" << endl;
335  fermat.Execute(ss.str());
336  ss.clear();
337  ss.str("");
338 
339  ss << "[m]:=[(";
340  for(int c=0; c<ncol; c++) {
341  for(int r=0; r<nrow; r++) {
342  ss << mat(r,c).subs(iEpsilon==0,nopat).subs(v2f,nopat) << ",";
343  }
344  }
345  for(int r=0; r<nrow; r++) ss << "0,";
346  ss << ")];" << endl;
347  ss << "Redrowech([m]);" << endl;
348  auto tmp = ss.str();
349  string_replace_all(tmp,",)]",")]");
350  fermat.Execute(tmp);
351  ss.clear();
352  ss.str("");
353 
354  ss << "&(U=1);" << endl; // ugly printing, the whitespace matters
355  ss << "![m" << endl;
356  auto ostr = fermat.Execute(ss.str());
357  ss.clear();
358  ss.str("");
359  //fermat.Exit();
360 
361  // note the order, before exfactor (normal_fermat will be called again here)
362  ss << "&(U=0);" << endl; // disable ugly printing
363  ss << "@([m]);" << endl;
364  ss << "&_G;" << endl;
365  fermat.Execute(ss.str());
366  ss.clear();
367  ss.str("");
368 
369  // make sure last char is 0
370  if(ostr[ostr.length()-1]!='0') throw Error("Apart: last char is NOT 0.");
371  ostr = ostr.substr(0, ostr.length()-1);
372  string_trim(ostr);
373 
374  ostr.erase(0, ostr.find(":=")+2);
375  string_replace_all(ostr, "[", "{");
376  string_replace_all(ostr, "]", "}");
377  Parser fp(st);
378  auto mat2 = fp.Read(ostr);
379 
380  exmap xs;
381  for(int c=0; c<ncol; c++) xs[c] = iWF(c);
382  for(int r=nrow-1; r>=0; r--) {
383  ex xadd = 0;
384  int pi=-1;
385  for(int c=0; c<ncol; c++) {
386  if(!is_zero(get_op(mat2,r,c)) && pi<0) pi = c;
387  else xadd -= get_op(mat2,r,c)*xs[c];
388  }
389  xs[pi] = xadd;
390  }
391  for(int c=0; c<ncol; c++) null_vec.append(xs[c]);
392  } else {
393  matrix v(ncol, 1);
394  exmap sRepl;
395  for (int c=0; c<ncol; c++) {
396  symbol t;
397  v(c,0) = t;
398  sRepl[t]=iWF(c);
399  }
400  // Solve M*V = 0
401  matrix zero(nrow, 1);
402  matrix s = ex_to<matrix>(key.subs(iEpsilon==0,nopat)).solve(v,zero);
403  for(int r=0; r<ncol; r++) null_vec.append(s(r,0).subs(sRepl,nopat));
404  }
405  null_cache.insert({key,null_vec});
406  } else {
407  null_vec = ex_to<lst>(null_itr->second);
408  }
409 
410  // check null & return ApartIR
411  bool is_null = true;
412  for(int c=0; c<ncol; c++) {
413  if(!is_zero(null_vec.op(c))) {
414  is_null = false;
415  break;
416  }
417  }
418  if(is_null) {
419  ex res = ApartIR(mat);
420  if(using_cache) mat_cache.insert({mat,res});
421  return res;
422  }
423 
424  // handle numerator
425  int ni=-1;
426  for(int c=0; c<ncol; c++) {
427  if(mat(nrow+1,c)>0 && !is_zero(null_vec.op(c))) {
428  ni = c;
429  break;
430  }
431  }
432 
433  if(ni!=-1) {
434  ex nvec;
435  if(true) {
436  exset wfs;
437  find(null_vec.op(ni),iWF(w),wfs);
438  if(wfs.size()<1) throw Error("Apart: something is wrong!");
439  symbol s;
440 
441  int max = -1;
442  for(auto wf : wfs) {
443  auto n1 = subs(null_vec, wf==s);
444  n1 = subs(n1, iWF(w)==0);
445  for(int i=0; i<3; i++) {
446  auto n2 = subs(n1, s==i);
447  if(!is_zero(n2.op(ni))) {
448  int nt = 0;
449  for(auto ii : n2) if(ii.is_zero()) nt++;
450  if(nt>max && nt!=ncol) { nvec = n2; max = nt; }
451  break;
452  }
453  }
454  }
455  if(nvec.has(iWF(w)) || is_zero(nvec.op(ni))) throw Error("Apart: iWF to int failed with "+ex2str(null_vec.op(ni)));
456  }
457 
458  ex sol = 0;
459  for(int c=0; c<ncol; c++) {
460  if(c==ni) continue;
461  sol -= nvec.op(c) * (iWF(c)-mat(nrow,c)); // iWF(c) refer to c-th column, and minus the const term
462  }
463  sol = sol/nvec.op(ni) + mat(nrow,ni); // last one: const term
464  sol = collect_ex(pow(sol.subs(iEpsilon==0,nopat), mat(nrow+1,ni)), iWF(w)); // expand the numerator with power
465 
466  if(!is_a<add>(sol)) sol = lst{ sol };
467  ex res = 0;
468  for(auto item : sol) {
469  int nzero = 0;
470  matrix mat2(nrow+2, ncol-1);
471  for(int c=0; c<ncol; c++) {
472  if(c==ni) continue;
473  int c2 = (c<ni ? c : c-1);
474  for(int r=0; r<nrow+1; r++) {
475  mat2(r,c2) = mat(r,c);
476  }
477  auto expn = mat(nrow+1,c) + item.degree(iWF(c));
478  if(is_zero(expn)) nzero++;
479  mat2(nrow+1,c2) = expn;
480  }
481  if(nzero>0) {
482  if((ncol-1-nzero)==0) {
483  res += ApartIR(1) * item.subs(iWF(w)==1);
484  } else {
485  matrix mat3(nrow+2, ncol-1-nzero);
486  int cc=0;
487  for(int c=0; c<ncol-1; c++) {
488  if(is_zero(mat2(nrow+1,c))) continue;
489  for(int r=0; r<nrow+2; r++) {
490  mat3(r,cc) = mat2(r,c);
491  }
492  cc++;
493  }
494  res += Apart(mat3) * item.subs(iWF(w)==1);
495  }
496  } else {
497  res += Apart(mat2) * item.subs(iWF(w)==1);
498  }
499  }
500  res = collect_ex(res,ApartIR(w));
501  if(using_cache) mat_cache.insert({mat,res});
502  return res;
503  }
504 
505  // handle all denominators
506  ex cres0 = 0;
507  for(int c=0; c<ncol; c++) cres0 += mat(nrow,c)*null_vec.op(c);
508  cres0 = cres0.subs(iEpsilon==0,nopat);
509 
510  ex nvec,cres;
511  if(true) {
512  exset wfs;
513  find(lst{cres0, null_vec},iWF(w),wfs);
514  if(wfs.size()<1) {
515  cres = cres0;
516  nvec = null_vec;
517  } else {
518  symbol s;
519  int max = -1;
520  bool c0 = true;
521  for(auto wf : wfs) {
522  auto c1 = subs(cres0, wf==s);
523  auto n1 = subs(null_vec, wf==s);
524  c1 = subs(c1, iWF(w)==0);
525  n1 = subs(n1, iWF(w)==0);
526  for(int i=0; i<5; i++) {
527  auto c2 = subs(c1, s==i).normal();
528  auto n2 = subs(n1, s==i);
529  if(!c0 && c2.is_zero()) continue;
530  if(c0 && !c2.is_zero()) {
531  c0 = false;
532  nvec = n2; cres = c2;
533  } else {
534  int nt = 0;
535  for(auto ii : n2) if(ii.is_zero()) nt++;
536  if(nt>max && nt!=ncol) { nvec = n2; cres = c2; max = nt; }
537  }
538  }
539  }
540  }
541  } // end if(true)
542 
543  if(nvec.has(iWF(w)) || cres.has(iWF(w))) {
544  cout << cres0 << ", " << null_vec << endl;
545  throw Error("Apart: iWF still left.");
546  }
547 
548  // handle const is NOT zero
549  if(!IsZero(cres)) {
550  ex res=0;
551  for(int c=0; c<ncol; c++) {
552  if(is_zero(nvec.op(c))) continue;
553  if(is_zero(mat(nrow+1,c)+1)) {
554  matrix mat2(nrow+2,ncol-1);
555  int ccc = 0;
556  for(int cc=0; cc<ncol; cc++) {
557  if(cc==c) continue;
558  for(int r=0; r<nrow+2; r++) mat2(r,ccc)=mat(r,cc);
559  ccc++;
560  }
561  res += Apart(mat2) * nvec.op(c);
562  } else {
563  matrix mat2(mat);
564  mat2(nrow+1,c) = mat2(nrow+1,c)+1;
565  res += Apart(mat2) * nvec.op(c);
566  }
567  }
568  res = res/cres;
569  res = collect_ex(res,ApartIR(w));
570  if(using_cache) mat_cache.insert({mat,res});
571  return res;
572  } else {
573  int ni=-1;
574  for(int c=0; c<ncol; c++) {
575  if(!is_zero(nvec.op(c))) {
576  ni = c;
577  break;
578  }
579  }
580 
581  ex res=0;
582  for(int c=0; c<ncol; c++) {
583  if(is_zero(nvec.op(c)) || c==ni) continue;
584  if(is_zero(mat(nrow+1,c)+1)) {
585  matrix mat2(nrow+2,ncol-1);
586  int ccc = 0;
587  for(int cc=0; cc<ncol; cc++) {
588  if(cc==c) continue;
589  for(int r=0; r<nrow+2; r++) mat2(r,ccc)=mat(r,cc);
590  ccc++;
591  }
592  int ni2 = ni>c ? ni-1 : ni;
593  mat2(nrow+1,ni2) = mat2(nrow+1,ni2)-1;
594  res -= Apart(mat2) * nvec.op(c);
595  } else {
596  matrix mat2(mat);
597  mat2(nrow+1,c) = mat2(nrow+1,c)+1;
598  mat2(nrow+1,ni) = mat2(nrow+1,ni)-1;
599  res -= Apart(mat2) * nvec.op(c);
600  }
601  }
602  res = res/nvec.op(ni);
603  res = collect_ex(res,ApartIR(w));
604  if(using_cache) mat_cache.insert({mat,res});
605  return res;
606  }
607  }
608 
616  ex Apart(const ex &expr_ino, const lst &vars_in, exmap smap) {
617  // Apart on rational terms
618  if(!is_a<lst>(expr_ino)) {
619  auto cv_lst = collect_lst(expr_ino, vars_in);
620  ex res = 0;
621  for(auto item : cv_lst) res += item.op(0) * Apart(lst{item.op(1)}, vars_in, smap);
622  res = collect_ex(res, ApartIR(w1,w2));
623 
624  // random check
625  lst nlst;
626  for(const_preorder_iterator i = res.preorder_begin(); i != res.preorder_end(); ++i) {
627  auto e = (*i);
628  if(is_a<symbol>(e) || is_a<Pair>(e)) nlst.append(e);
629  }
630  for(auto var : vars_in) nlst.append(var);
631  nlst.sort();
632  nlst.unique();
633  exmap nrepl;
634  auto pi = nextprime(3);
635  for(auto ni : nlst) {
636  pi = nextprime(pi+1);
637  nrepl[ni] = ex(1)/pi;
638  }
639  nrepl[iEpsilon]=0;
640  ex chk = ApartIR2ex(subs(res,nrepl))-subs(expr_ino,nrepl);
641  chk = exnormal(chk);
642  if(!is_zero(chk)) throw Error("Apart@1 random check Failed.");
643  return res;
644  }
645 
646  // Apart on monomial term
647  if(expr_ino.nops()!=1) throw Error("Apart: wrong convention found!");
648  ex expr_in = expr_ino.op(0);
649 
650  static exmap cache;
651  if(using_cache && cache_limit>0 && cache.size() > cache_limit) cache.clear();
652  auto itr = cache.find(expr_in);
653  if(itr!=cache.end()) return itr->second;
654 
655  exmap map1, map2;
656  lst vars;
657  for(int i=0; i<vars_in.nops(); i++) {
658  auto v = vars_in.op(i);
659  Symbol s("_apX"+to_string(i));
660  map1[v]=s;
661  map2[s]=v;
662  vars.append(s);
663  }
664  exmap sgnmap;
665  for(auto kv : smap) sgnmap[kv.first.subs(map1,nopat)] = kv.second.subs(map1,nopat);
666 
667  ex expr = expr_in.subs(map1,nopat);
668  if(!is_a<mul>(expr)) expr = lst{expr};
669 
670  // check only, try normal_fermat if faild
671  bool ok = true;
672  for(auto item : expr) {
673  bool has_var=false;
674  for(auto v : vars) {
675  if(item.has(v)) {
676  has_var=true;
677  break;
678  }
679  }
680  if(has_var) {
681  if(!isOK(item,vars)) {
682  ok = false;
683  break;
684  }
685  }
686  }
687  if(!ok) {
688  expr = expr_in.subs(map1,nopat);
689  expr = exnormal(expr,o_flintfD); // need option factor=true, factor denominator
690  if(!is_a<mul>(expr)) expr = lst{expr};
691  }
692 
693  lst pnlst;
694  ex pref = 1;
695  map<ex,int,ex_is_less> count_ip;
696  exmap ie_map;
697  for(auto item : expr) {
698  bool has_var=false;
699  for(auto v : vars) {
700  if(item.has(v)) {
701  has_var=true;
702  break;
703  }
704  }
705  if(has_var) {
706  if(!isOK(item,vars)) {
707  cout << expr_in << endl;
708  cout << item << endl;
709  throw Error("Apart: item is not linear wrt vars.");
710  }
711 
712  ex pc, nc;
713  if(is_a<power>(item)) {
714  pc = item.op(0);
715  nc = item.op(1);
716  } else {
717  pc = item;
718  nc = 1;
719  }
720 
721  // consider sign
722  bool has_sgn = false;
723  // iEpsilon
724  if(pc.has(iEpsilon)) { // iEpsilon first
725  ex si = 1; // default
726  auto itr = sgnmap.find(iEpsilon);
727  if(itr != sgnmap.end() && !is_zero(itr->second)) si = itr->second;
728  ex sign = si/pc.coeff(iEpsilon);
729  pref /= pow(sign, nc);
730  pc *= sign;
731  has_sgn = true;
732  }
733  // v from sgnmap
734  if(!has_sgn) {
735  for(auto v : vars) {
736  auto cc = pc.coeff(v);
737  if(is_zero(cc) || !key_exists(sgnmap,v) || is_zero(sgnmap[v])) continue;
738  ex sign = sgnmap[v]/cc;
739  pref /= pow(sign, nc);
740  pc *= sign;
741  has_sgn = true;
742  break;
743  }
744  }
745  // v from vars
746  if(!has_sgn) {
747  for(auto v : vars) {
748  if(key_exists(sgnmap,v)) continue; // already handled by sgnmap
749  auto cc = pc.coeff(v);
750  if(is_zero(cc) || !is_a<numeric>(cc)) continue;
751  ex sign = 1/cc;
752  pref /= pow(sign, nc);
753  pc *= sign;
754  break;
755  }
756  }
757 
758  ex key = expand(pc.subs(iEpsilon==0,nopat));
759  if(pc.has(iEpsilon)) {
760  if(ie_map.find(-key) != ie_map.end()) {
761  cout << expr_ino << endl;
762  cout << "Item 1: " << ie_map[-key].subs(map2,nopat) << endl;
763  cout << "Item 2: " << pc.subs(map2,nopat) << endl;
764  throw Error("iEpsilon Error: maybe pinch singularity?");
765  }
766  ie_map[key] = pc;
767  }
768 
769  auto itr = count_ip.find(key);
770  if(itr==count_ip.end()) itr = count_ip.find(-key);
771  if(itr==count_ip.end()) count_ip[key] = 1;
772  else itr->second++;
773 
774  pnlst.append(lst{ pc, nc });
775  } else pref *= item;
776  }
777  // check whether needs to combine again
778  bool needs_again = false;
779  for(auto kv : count_ip) {
780  if(kv.second>1) { needs_again = true; break; }
781  }
782  if(needs_again) {
783  exmap imap;
784  for(auto pn : pnlst) {
785  auto key = pn.op(0);
786  if(key.has(iEpsilon)) {
787  auto k = key.subs(iEpsilon==0,nopat);
788  if(imap.find(-k) != imap.end()) {
789  cout << "Item 1: " << imap[k] << endl;
790  cout << "Item 2: " << key << endl;
791  throw Error("iEpsilon Error: maybe pinch singularity?");
792  }
793  if(imap.find(k)==imap.end()) imap[k] = key;
794  }
795  }
796  exmap p2n;
797  for(auto pn : pnlst) {
798  auto key = pn.op(0);
799  auto itr = imap.find(key);
800  if(itr!=imap.end()) key = itr->second;
801  else {
802  itr = imap.find(-key);
803  if(itr!=imap.end()) {
804  key = itr->second;
805  pref *= pow(-1, pn.op(1));
806  }
807  }
808  p2n[key] = p2n[key] + pn.op(1);
809  }
810  pnlst.remove_all();
811  for(auto kv : p2n) {
812  if(is_zero(kv.second)) continue;
813  auto k = kv.first;
814  auto v = kv.second;
815  if(is_a<numeric>(v) && ex_to<numeric>(v)>0) {
816  k = k.subs(iEpsilon==0,nopat);
817  }
818  pnlst.append(lst{k, v});
819  }
820  }
821  sort_lst(pnlst); // need sort_lst
822 
823  if(pnlst.nops()==0) return cache[expr_in] = pref * ApartIR(1,vars_in);
824 
825  int nrow=vars.nops(), ncol=pnlst.nops();
826  exmap vars0;
827  for(auto v : vars) vars0[v]=0;
828  matrix mat(nrow+2, ncol);
829  for(int c=0; c<ncol; c++) {
830  ex pn = pnlst.op(c);
831  if(pn.op(0).is_zero() && pn.op(1).is_zero()) throw Error("Apart: 0^0 is found!");
832  mat(nrow+1,c) = normal(pn.op(1));
833  auto tmp = pn.op(0);
834  for(int r=0; r<nrow; r++) {
835  mat(r,c) = exnormal(tmp.coeff(vars.op(r)));
836  }
837  mat(nrow,c) = exnormal(tmp.subs(vars0,nopat));
838  }
839 
840  ex ret = Apart(mat);
841 
842  auto cv_lst = collect_lst(ret,ApartIR(w));
843  ret = 0;
844  for(auto cv : cv_lst) {
845  ret += pref * cv.op(0) * ApartIR(cv.op(1).op(0), vars).subs(map2,nopat);
846  }
847 
848  return cache[expr_in] = ret;
849  }
850 
859  ex Apart(const ex &expr_ino, const lst &loops, const lst & extps, exmap smap) {
860  auto expr_in = expr_ino.subs(SP_map,nopat);
861  auto expr = expr_in;
862 
863  lst sps;
864  for(auto li : loops) {
865  for(auto li2: loops) {
866  auto item = SP(li, li2).subs(SP_map,nopat);
867  if(is_a<Pair>(item)) sps.append(item);
868  }
869  for(auto ei: extps) {
870  auto item = SP(li, ei).subs(SP_map,nopat);
871  if(is_a<Pair>(item)) sps.append(item);
872  }
873  }
874  sps.sort();
875  sps.unique();
876 
877  auto cv_lst = collect_lst(expr, loops);
878  ex res = 0;
879  for(auto item : cv_lst) {
880  res += item.op(0) * Apart(lst{item.op(1)}, sps, smap);
881  }
882 
883  res = collect_ex(res, ApartIR(w1,w2));
884 
885  // random check
886  lst nlst;
887  for(const_preorder_iterator i = res.preorder_begin(); i != res.preorder_end(); ++i) {
888  auto e = (*i);
889  if(is_a<symbol>(e) || is_a<Pair>(e)) nlst.append(e);
890  }
891  nlst.sort();
892  nlst.unique();
893  exmap nrepl;
894  auto pi = nextprime(3);
895  for(auto ni : nlst) {
896  pi = nextprime(pi+1);
897  nrepl[ni] = 1/pi;
898  }
899  nrepl[iEpsilon]=0;
900  ex chk = ApartIR2ex(subs(res,nrepl))-subs(expr_in,nrepl);
901  chk = normal(chk);
902  if(!is_zero(chk)) {
903  throw Error("Apart@2 random check Failed.");
904  }
905 
906  return res;
907  }
908 
914  ex ApartIRC(const ex & expr_in) {
915  exmap cache;
916  MapFunction map([&cache](const ex & e, MapFunction &self)->ex {
917  if(!e.has(ApartIR(w1,w2))) return e;
918  else if(e.match(ApartIR(w1,w2))) {
919  auto i = cache.find(e);
920  if(i!=cache.end()) return i->second;
921  int n = e.op(1).nops();
922  if((e.op(0)).is_equal(1)) {
923  matrix mat(n+2,n);
924  for(int r=0; r<n+2; r++) {
925  for(int c=0; c<n; c++) mat(r,c) = 0;
926  }
927  for(int i=0; i<n; i++) mat(i,i) = 1;
928  return cache[e] = ApartIR(mat, e.op(1));
929  }
930  if(!is_a<matrix>(e.op(0))) throw Error("ApartIRC: Not matrix : " + ex2str(e.op(0)));
931  auto mat0 = ex_to<matrix>(e.op(0));
932  matrix mat(n+2,n);
933  int cc = mat0.cols();
934  if(cc==n) mat=mat0;
935  else {
936  // zero each element
937  for(int r=0; r<n+2; r++) {
938  for(int c=0; c<cc; c++) mat(r,c) = 0;
939  }
940  // n-row from mat0 to mat, note the last 2 rows still 0
941  for(int r=0; r<n; r++) {
942  for(int c=0; c<cc; c++) mat(r,c) = mat0(r,c);
943  }
944  for(int i=0; i<n; i++) {
945  mat(i,cc) = 1;
946  auto r = mat.rank();
947  if(r==n) break;
948  if(r==cc+1) cc++;
949  else mat(i,cc) = 0;
950  }
951  if(mat.rank()!=n) throw Error("ApartIRC failed, NOT full rank.");
952  // last 2 rows from mat0 to mat
953  for(int r=n; r<n+2; r++) {
954  for(int c=0; c<mat0.cols(); c++) mat(r,c) = mat0(r,c);
955  }
956  }
957  return cache[e] = ApartIR(mat, e.op(1));
958  } else return e.map(self);
959  });
960  return map(expr_in);
961  }
962 
963 
964 }
965 
966 
#define CHECK
HEP header file.
class used to wrap error message
Definition: BASIC.h:242
interface to communicate with Fermat program
Definition: BASIC.h:797
string Execute(string)
Definition: Process.cpp:102
class to wrap map_function of GiNaC
Definition: BASIC.h:632
class to parse for string or file, helpful with Symbol class
Definition: BASIC.h:645
ex Read(const string &instr, bool s2S=true)
Definition: Functions.cpp:111
class extended to GiNaC symbol class, represent a positive symbol
Definition: BASIC.h:113
ex subs(const exmap &m, unsigned options=0) const override
Definition: BASIC.h:145
do_not_evalf_params().expl_derivative_func(zd1D).derivative_func(zp1D)) REGISTER_FUNCTION(FTX
HepLib namespace.
Definition: BASIC.cpp:17
const iSymbol iEpsilon
ex pn2p(ex pn)
Definition: Apart.cpp:145
ex pn2mat(ex ps, ex ns, ex vars)
Definition: Apart.cpp:109
ex Apart(const ex &expr_ino, const lst &loops, const lst &extps, exmap smap)
Apart on ex.
Definition: Apart.cpp:859
exmap SP_map
Definition: Init.cpp:182
ex ApartIR2F(const ex &expr_in)
convert ApartIR to F(ps, ns), ns is like FIRE convention
Definition: Apart.cpp:61
ex ApartIRC(const ex &expr_in)
complete the ApartIR elements
Definition: Apart.cpp:914
ex collect_ex(ex const &expr_in, std::function< bool(const ex &)> has_func, int opt)
the collect function like Mathematica
Definition: BASIC.cpp:1202
bool Apart_using_fermat
Definition: Init.cpp:210
ex ApartIR2ex(const ex &expr_in)
convert ApartIR to ex
Definition: Apart.cpp:35
bool key_exists(const exmap &map, const ex &key)
Definition: BASIC.h:293
bool using_cache
Definition: Init.cpp:156
ex air2pn(ex air)
Definition: Apart.cpp:89
ex w
Definition: Init.cpp:93
const Symbol vs
ex get_op(const ex ex_in, int index1, int index2)
return index1-th.index2-th of expression
Definition: BASIC.cpp:1606
ex exnormal(const ex &expr, int opt)
normalize a expression
Definition: BASIC.cpp:1916
const int o_flintfD
Definition: Init.cpp:115
ex nextprime(const ex &n)
Definition: BASIC.cpp:2284
lst collect_lst(ex const &expr_in, std::function< bool(const ex &)> has_func, int opt)
the collect function like Mathematica, reture the lst { {c1,v1}, {c2,v2}, ... }
Definition: BASIC.cpp:1222
exmap ApartRules(const exvector &airs, bool irc)
Definition: Apart.cpp:156
void sort_lst(lst &ilst, bool less=true)
sort the list in less order, or the reverse
Definition: Sort.cpp:79
void string_replace_all(string &str, const string &from, const string &to)
Definition: Functions.cpp:148
bool is_subset(const ex &e1, const ex &e2)
Definition: Apart.cpp:130
string ex2str(const ex &expr)
convert ex to output string, the defalut printer format will be used
Definition: BASIC.cpp:715
bool IsZero(const ex &e)
long long cache_limit
Definition: Init.cpp:157
ex w1
Definition: BASIC.h:499
unsigned nopat
Definition: Init.cpp:91
exvector GiNaC_Parallel(int ntotal, std::function< ex(int)> f, const string &key)
GiNaC Parallel Evaluation using fork.
Definition: BASIC.cpp:260
ex w2
Definition: BASIC.h:499
void string_trim(string &str)
Definition: Functions.cpp:156
ex SP(const ex &a, bool use_map=false)
Definition: Pair.cpp:166
ex subs(const ex &e, init_list sl)
Definition: BASIC.h:51