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