1 /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2    Copyright (c) 2012-2021 The plumed team
3    (see the PEOPLE file at the root of the distribution for a list of names)
4 
5    See http://www.plumed.org for more information.
6 
7    This file is part of plumed, version 2.
8 
9    plumed is free software: you can redistribute it and/or modify
10    it under the terms of the GNU Lesser General Public License as published by
11    the Free Software Foundation, either version 3 of the License, or
12    (at your option) any later version.
13 
14    plumed is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU Lesser General Public License for more details.
18 
19    You should have received a copy of the GNU Lesser General Public License
20    along with plumed.  If not, see <http://www.gnu.org/licenses/>.
21 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
22 #include "LatticeReduction.h"
23 #include "Exception.h"
24 #include <cstdio>
25 #include <cmath>
26 
27 namespace PLMD {
28 
29 using namespace std;
30 
31 const double epsilon=1e-14;
32 
sort(Vector v[3])33 void LatticeReduction::sort(Vector v[3]) {
34   const double onePlusEpsilon=(1.0+epsilon);
35   for(int i=0; i<3; i++) for(int j=i+1; j<3; j++) if(modulo2(v[i])>modulo2(v[j])) {
36         Vector x=v[i]; v[i]=v[j]; v[j]=x;
37       }
38   for(int i=0; i<2; i++) plumed_assert(modulo2(v[i])<=modulo2(v[i+1])*onePlusEpsilon);
39 }
40 
reduce(Vector & a,Vector & b)41 void LatticeReduction::reduce(Vector&a,Vector&b) {
42   const double onePlusEpsilon=(1.0+epsilon);
43   double ma=modulo2(a);
44   double mb=modulo2(b);
45   unsigned counter=0;
46   while(true) {
47     if(mb>ma) {
48       Vector t(a); a=b; b=t;
49       double mt(ma); ma=mb; mb=mt;
50     }
51     a-=b*floor(dotProduct(a,b)/modulo2(b)+0.5);
52     ma=modulo2(a);
53     if(mb<=ma*onePlusEpsilon) break;
54     counter++;
55     if(counter%100==0) { // only test rarely since this might be expensive
56       plumed_assert(!std::isnan(ma));
57       plumed_assert(!std::isnan(mb));
58     }
59     if(counter%10000==0) fprintf(stderr,"WARNING: LatticeReduction::reduce stuck after %u iterations\n",counter);
60   }
61 
62   Vector t(a); a=b; b=t;
63 }
64 
reduce2(Vector & a,Vector & b,Vector & c)65 void LatticeReduction::reduce2(Vector&a,Vector&b,Vector&c) {
66   Vector v[3];
67   v[0]=a; v[1]=b; v[2]=c;
68   int iter=0;
69   int ok=0;
70   while(ok<3) {
71     int i,j;
72     if(iter%3==0) {
73       i=0; j=1;
74     } else if(iter%3==1) {
75       i=0; j=2;
76     } else {
77       i=1; j=2;
78     }
79     if(isReduced(v[i],v[j])) ok++;
80     else {
81       reduce(v[i],v[j]);
82       ok=1;
83     }
84     iter++;
85   }
86   a=v[0]; b=v[1]; c=v[2];
87 }
88 
isReduced(const Vector & a,const Vector & b)89 bool LatticeReduction::isReduced(const Vector&a,const Vector&b) {
90   const int cut=5;
91   for(int i=-cut; i<=cut; i++) {
92     if(modulo2(b+i*a)<modulo2(b)) return false;
93   }
94   return modulo2(a)<=modulo2(b) && 2.0*dotProduct(a,b)<=modulo2(a);
95 }
96 
reduce2(Tensor & t)97 void LatticeReduction::reduce2(Tensor&t) {
98   Vector a=t.getRow(0);
99   Vector b=t.getRow(1);
100   Vector c=t.getRow(2);
101   reduce2(a,b,c);
102   t.setRow(0,a);
103   t.setRow(1,b);
104   t.setRow(2,c);
105 }
106 
reduce(Tensor & t)107 void LatticeReduction::reduce(Tensor&t) {
108   reduceFast(t);
109 }
110 
reduceFast(Tensor & t)111 void LatticeReduction::reduceFast(Tensor&t) {
112   const double onePlusEpsilon=(1.0+epsilon);
113   Vector v[3];
114   v[0]=t.getRow(0);
115   v[1]=t.getRow(1);
116   v[2]=t.getRow(2);
117   unsigned counter=0;
118   while(true) {
119     sort(v);
120     reduce(v[0],v[1]);
121     double b11=modulo2(v[0]);
122     double b22=modulo2(v[1]);
123     double b12=dotProduct(v[0],v[1]);
124     double b13=dotProduct(v[0],v[2]);
125     double b23=dotProduct(v[1],v[2]);
126     double z=b11*b22-b12*b12;
127     double y2=-(b11*b23-b12*b13)/z;
128     double y1=-(b22*b13-b12*b23)/z;
129     int x1min=floor(y1);
130     int x1max=x1min+1;
131     int x2min=floor(y2);
132     int x2max=x2min+1;
133     bool first=true;
134     double mbest,mtrial;
135     Vector trial,best;
136     for(int x1=x1min; x1<=x1max; x1++)
137       for(int x2=x2min; x2<=x2max; x2++) {
138         trial=v[2]+x2*v[1]+x1*v[0];
139         mtrial=modulo2(trial);
140         if(first || mtrial<mbest) {
141           mbest=mtrial;
142           best=trial;
143           first=false;
144         }
145       }
146     if(modulo2(best)*onePlusEpsilon>=modulo2(v[2])) break;
147     counter++;
148     if(counter%10000==0) fprintf(stderr,"WARNING: LatticeReduction::reduceFast stuck after %u iterations\n",counter);
149     v[2]=best;
150   }
151   sort(v);
152   t.setRow(0,v[0]);
153   t.setRow(1,v[1]);
154   t.setRow(2,v[2]);
155 }
156 
157 
reduceSlow(Tensor & t)158 void LatticeReduction::reduceSlow(Tensor&t) {
159   Vector v[3];
160   v[0]=t.getRow(0);
161   v[1]=t.getRow(1);
162   v[2]=t.getRow(2);
163   reduce2(v[0],v[1],v[2]);
164   double e01=dotProduct(v[0],v[1]);
165   double e02=dotProduct(v[0],v[2]);
166   double e12=dotProduct(v[1],v[2]);
167   if(e01*e02*e12<0) {
168     int eps01=0; if(e01>0.0) eps01=1; else if(e01<0.0) eps01=-1;
169     int eps02=0; if(e02>0.0) eps02=1; else if(e02<0.0) eps02=-1;
170     Vector n=v[0]-eps01*v[1]-eps02*v[2];
171     int i=0; double mx=modulo2(v[i]);
172     for(int j=1; j<3; j++) {
173       double f=modulo2(v[j]);
174       if(f>mx) {
175         i=j;
176         mx=f;
177       }
178     }
179     if(modulo2(n)<mx) v[i]=n;
180   }
181   sort(v);
182   t.setRow(0,v[0]);
183   t.setRow(1,v[1]);
184   t.setRow(2,v[2]);
185 }
186 
isReduced2(const Vector & a,const Vector & b,const Vector & c)187 bool LatticeReduction::isReduced2(const Vector&a,const Vector&b,const Vector &c) {
188   return isReduced(a,b) && isReduced(a,b) && isReduced(b,c);
189 }
190 
isReduced(const Tensor & t)191 bool LatticeReduction::isReduced(const Tensor&t) {
192   Vector v[3];
193   double m[3];
194   v[0]=t.getRow(0);
195   v[1]=t.getRow(1);
196   v[2]=t.getRow(2);
197   for(int i=0; i<3; i++) m[i]=modulo2(v[i]);
198   if(!((m[0]<=m[1]) && m[1]<=m[2])) return false;
199   const int cut=5;
200   for(int i=-cut; i<=cut; i++) {
201     double mm=modulo2(v[1]+i*v[0]);
202     if(mm<m[1]) return false;
203     for(int j=-cut; j<=cut; j++) {
204       double mx=modulo2(v[2]+i*v[1]+j*v[0]);
205       if(mx<m[2])return false;
206     }
207   }
208   return true;
209 }
210 
211 }
212