1 | //////////////////////////////////////////////////////////////////// |
---|
2 | version="version modular.lib 4.0.0.0 May_2014 "; // $Id$ |
---|
3 | category="General purpose"; |
---|
4 | info=" |
---|
5 | LIBRARY: modular.lib An abstraction layer for modular techniques |
---|
6 | AUTHOR: Andreas Steenpass, e-mail: steenpass@mathematik.uni-kl.de |
---|
7 | |
---|
8 | OVERVIEW: |
---|
9 | This library is an abstraction layer for modular techniques which are |
---|
10 | well-known to speed up many computations and to be easy parallelizable. |
---|
11 | @* The basic idea is to execute some computation modulo several primes and then |
---|
12 | to lift the result back to characteristic zero via the farey rational map and |
---|
13 | chinese remaindering. It is thus possible to overcome the often problematic |
---|
14 | coefficient swell and to run the modular computations in parallel. |
---|
15 | @* In Singular, modular techniques have been quite successfully employed for |
---|
16 | several applications. A first implementation was done for Groebner bases in |
---|
17 | Singular's @ref{modstd_lib}, a pioneering work by Stefan Steidel. Since the |
---|
18 | algorithm is basically the same for all applications, this library aims at |
---|
19 | preventing library authors from writing the same code over and over again by |
---|
20 | providing an appropriate abstraction layer. It also offers one-line commands |
---|
21 | for ordinary Singular users who want to take advantage of modular techniques |
---|
22 | for their own calculations. Thus modular techniques can be regarded as |
---|
23 | a parallel skeleton of their own. |
---|
24 | @* The terminology (such as 'pTest' and 'finalTest') follows Singular's |
---|
25 | @ref{modstd_lib} and [1]. |
---|
26 | |
---|
27 | REFERENCES: |
---|
28 | [1] Nazeran Idrees, Gerhard Pfister, Stefan Steidel: Parallelization of |
---|
29 | Modular Algorithms. Journal of Symbolic Computation 46, 672-684 (2011). |
---|
30 | http://arxiv.org/abs/1005.5663 |
---|
31 | |
---|
32 | SEE ALSO: link, tasks_lib, parallel_lib, modstd_lib, assprimeszerodim_lib |
---|
33 | |
---|
34 | KEYWORDS: modular_lib; Modular techniques; Parallelization; |
---|
35 | Skeletons for parallelization; Distributed computing |
---|
36 | |
---|
37 | PROCEDURES: |
---|
38 | modular(...) execute a command modulo several primes and lift the result |
---|
39 | back to characteristic zero |
---|
40 | "; |
---|
41 | |
---|
42 | LIB "resources.lib"; |
---|
43 | LIB "tasks.lib"; |
---|
44 | LIB "parallel.lib"; |
---|
45 | |
---|
46 | static proc mod_init() |
---|
47 | { |
---|
48 | if (!defined(Resources)) { |
---|
49 | LIB "resources.lib"; |
---|
50 | } |
---|
51 | int sem_cores = Resources::sem_cores; // the number of processor cores |
---|
52 | exportto(Modular, sem_cores); |
---|
53 | } |
---|
54 | |
---|
55 | proc modular(string Command, alias list Arguments, list #) |
---|
56 | "USAGE: modular(command, arguments[, primeTest, deleteUnluckyPrimes, pTest, |
---|
57 | finalTest, pmax), command string, arguments list, primeTest proc, |
---|
58 | deleteUnluckyPrimes proc, pTest proc, finalTest proc, pmax int |
---|
59 | RETURN: the result of @code{command} applied to @code{arguments}, |
---|
60 | computed using modular methods. |
---|
61 | NOTE: For the general algorithm and the role of the optional arguments |
---|
62 | primeTest, deleteUnluckyPrimes, pTest, and finalTest, see |
---|
63 | @ref{modStd} and the reference given in @ref{modular_lib}. The |
---|
64 | default for these arguments is that all tests succeed and that all |
---|
65 | primes are assumed to be lucky. |
---|
66 | @* The optional argument pmax is an upper bound for the prime numbers |
---|
67 | to be used for the modular computations. The default is 2147483647 |
---|
68 | (largest prime which can be represented as an @code{int} in |
---|
69 | Singular), or 536870909 (largest prime below 2^29} for baserings with |
---|
70 | parameters. |
---|
71 | SEE ALSO: modStd |
---|
72 | EXAMPLE: example modular; shows an example" |
---|
73 | { |
---|
74 | /* check for errors */ |
---|
75 | if (char(basering) != 0) { |
---|
76 | ERROR("The characteristic must be zero."); |
---|
77 | } |
---|
78 | |
---|
79 | /* auxiliary variables */ |
---|
80 | int i; |
---|
81 | |
---|
82 | /* set maximal prime number */ |
---|
83 | int pmax = 2147483647; |
---|
84 | if (npars(basering) > 0) { |
---|
85 | pmax = 536870909; // prime(2^29) |
---|
86 | } |
---|
87 | |
---|
88 | /* read optional parameters */ |
---|
89 | list defaults = list(primeTest_default, deleteUnluckyPrimes_default, |
---|
90 | pTest_default, finalTest_default, pmax); |
---|
91 | for (i = 1; i <= size(defaults); i++) { |
---|
92 | if (typeof(#[i]) != typeof(defaults[i])) { |
---|
93 | # = insert(#, defaults[i], i-1); |
---|
94 | } |
---|
95 | } |
---|
96 | if (size(#) != size(defaults)) { |
---|
97 | ERROR("wrong optional parameters"); |
---|
98 | } |
---|
99 | proc primeTest = #[1]; |
---|
100 | proc deleteUnluckyPrimes = #[2]; |
---|
101 | proc pTest = #[3]; |
---|
102 | proc finalTest = #[4]; |
---|
103 | pmax = #[5]; |
---|
104 | exportto(Modular, primeTest); |
---|
105 | |
---|
106 | /* export command and arguments */ |
---|
107 | exportto(Modular, Command); |
---|
108 | exportto(Modular, Arguments); |
---|
109 | |
---|
110 | /* modular computations */ |
---|
111 | def result; |
---|
112 | def result_lift; |
---|
113 | bigint N = 1; |
---|
114 | list modresults; |
---|
115 | list primes; |
---|
116 | int nAllPrimes; |
---|
117 | int nNewPrimes; |
---|
118 | int p; |
---|
119 | list indices; |
---|
120 | int ncores_available; |
---|
121 | while (1) { |
---|
122 | // compute list of primes |
---|
123 | if (nAllPrimes == 0) { |
---|
124 | nNewPrimes = NbModProcs(); |
---|
125 | } |
---|
126 | else { |
---|
127 | ncores_available = system("semaphore", "get_value", sem_cores)+1; |
---|
128 | if (nAllPrimes < ncores_available) { |
---|
129 | nNewPrimes = nAllPrimes; |
---|
130 | } |
---|
131 | else { |
---|
132 | nNewPrimes = (nAllPrimes div ncores_available) |
---|
133 | *ncores_available; |
---|
134 | } |
---|
135 | } |
---|
136 | primes = primeList(nNewPrimes, pmax); |
---|
137 | pmax = primes[size(primes)]-1; |
---|
138 | nAllPrimes = nAllPrimes+nNewPrimes; |
---|
139 | |
---|
140 | // do computation modulo several primes |
---|
141 | for (i = size(primes); i > 0; i--) { |
---|
142 | task t(i) = "Modular::modp", primes[i]; |
---|
143 | } |
---|
144 | startTasks(t(1..size(primes))); |
---|
145 | waitAllTasks(t(1..size(primes))); |
---|
146 | for (i = size(primes); i > 0; i--) { |
---|
147 | modresults[i] = getResult(t(i)); |
---|
148 | killTask(t(i)); |
---|
149 | kill t(i); |
---|
150 | } |
---|
151 | |
---|
152 | // delete unlucky primes |
---|
153 | indices = deleteUnluckyPrimes(modresults); |
---|
154 | for (i = size(indices); i > 0; i--) { |
---|
155 | modresults = delete(modresults, indices[i]); |
---|
156 | primes = delete(primes, indices[i]); |
---|
157 | } |
---|
158 | |
---|
159 | // lift result |
---|
160 | if (N == 1) { |
---|
161 | result_lift = chinrem(modresults, primes); |
---|
162 | } |
---|
163 | else { |
---|
164 | result_lift = chinrem(list(result_lift)+modresults, |
---|
165 | list(N)+primes); |
---|
166 | } |
---|
167 | modresults = list(); |
---|
168 | for (i = size(primes); i > 0; i--) { |
---|
169 | N = N*primes[i]; |
---|
170 | } |
---|
171 | |
---|
172 | // apply farey |
---|
173 | result = farey_parallel(result_lift, N); |
---|
174 | |
---|
175 | // pTest |
---|
176 | p = prime(random(pmax div 2, pmax-1)); |
---|
177 | while (!Modular::primeTest(p, Arguments)) { |
---|
178 | if (p <= 2) { |
---|
179 | ERROR("no more primes"); |
---|
180 | } |
---|
181 | p = prime(random(p div 2, p-1)); |
---|
182 | } |
---|
183 | if (!pTest(Command, Arguments, result, p)) { |
---|
184 | continue; |
---|
185 | } |
---|
186 | |
---|
187 | // finalTest |
---|
188 | if (finalTest(Command, Arguments, result)) { |
---|
189 | break; |
---|
190 | } |
---|
191 | } |
---|
192 | |
---|
193 | /* kill exported data */ |
---|
194 | kill Command; |
---|
195 | kill Arguments; |
---|
196 | kill primeTest; |
---|
197 | |
---|
198 | /* return of result */ |
---|
199 | return(result); |
---|
200 | } |
---|
201 | example |
---|
202 | { |
---|
203 | "EXAMPLE:"; |
---|
204 | echo = 2; |
---|
205 | ring R = 0, (x,y), dp; |
---|
206 | ideal I = x9y2+x10, x2y7-y8; |
---|
207 | modular("std", list(I)); |
---|
208 | } |
---|
209 | |
---|
210 | static proc primeList(int n, int pmax) |
---|
211 | { |
---|
212 | list primes; |
---|
213 | int p = pmax; |
---|
214 | int i; |
---|
215 | for (i = 1; i <= n; i++) { |
---|
216 | if (p < 2) { |
---|
217 | ERROR("no more primes"); |
---|
218 | } |
---|
219 | p = prime(p); |
---|
220 | task t(i) = "Modular::primeList_task", list(p); |
---|
221 | p--; |
---|
222 | } |
---|
223 | startTasks(t(1..n)); |
---|
224 | waitAllTasks(t(1..n)); |
---|
225 | int j; |
---|
226 | for (i = 1; i <= n; i++) { |
---|
227 | if (getResult(t(i))) { |
---|
228 | j++; |
---|
229 | primes[j] = getArguments(t(i))[1]; |
---|
230 | } |
---|
231 | killTask(t(i)); |
---|
232 | } |
---|
233 | if (j < n) { |
---|
234 | primes = primes+primeList(n-j, p); |
---|
235 | } |
---|
236 | return(primes); |
---|
237 | } |
---|
238 | |
---|
239 | static proc primeList_task(int p) |
---|
240 | { |
---|
241 | return(Modular::primeTest(p, Arguments)); |
---|
242 | } |
---|
243 | |
---|
244 | static proc modp(int p) |
---|
245 | { |
---|
246 | def br = basering; |
---|
247 | list lbr = ringlist(br); |
---|
248 | if (typeof(lbr[1]) == "int") { |
---|
249 | lbr[1] = p; |
---|
250 | } |
---|
251 | else { |
---|
252 | lbr[1][1] = p; |
---|
253 | } |
---|
254 | def rp = ring(lbr); |
---|
255 | setring(rp); |
---|
256 | list args = fetch(br, Arguments); |
---|
257 | execute("def result = "+Command+"("+Tasks::argsToString("args", size(args)) |
---|
258 | +");"); |
---|
259 | setring(br); |
---|
260 | def result = fetch(rp, result); |
---|
261 | return(result); |
---|
262 | } |
---|
263 | |
---|
264 | static proc primeTest_default(int p, alias list args) |
---|
265 | { |
---|
266 | return(1); |
---|
267 | } |
---|
268 | |
---|
269 | static proc deleteUnluckyPrimes_default(alias list modresults) |
---|
270 | { |
---|
271 | return(list()); |
---|
272 | } |
---|
273 | |
---|
274 | static proc pTest_default(string command, alias list args, def result, int p) |
---|
275 | { |
---|
276 | return(1); |
---|
277 | } |
---|
278 | |
---|
279 | static proc finalTest_default(string command, alias list args, def result) |
---|
280 | { |
---|
281 | return(1); |
---|
282 | } |
---|
283 | |
---|
284 | static proc farey_parallel(ideal farey_arg, bigint farey_N) |
---|
285 | { |
---|
286 | exportto(Modular, farey_arg); |
---|
287 | exportto(Modular, farey_N); |
---|
288 | int size_arg = ncols(farey_arg); |
---|
289 | int chunks = par_range(size_arg); |
---|
290 | intvec range; |
---|
291 | int i; |
---|
292 | for (i = chunks; i > 0; i--) { |
---|
293 | range = par_range(size_arg, i); |
---|
294 | task t(i) = "Modular::farey_task", list(range); |
---|
295 | } |
---|
296 | startTasks(t(1..chunks)); |
---|
297 | waitAllTasks(t(1..chunks)); |
---|
298 | ideal result = getResult(t(chunks)); |
---|
299 | for (i = chunks-1; i > 0; i--) { |
---|
300 | result = getResult(t(i)), result; |
---|
301 | killTask(t(i)); |
---|
302 | } |
---|
303 | kill farey_arg; |
---|
304 | kill farey_N; |
---|
305 | return(result); |
---|
306 | } |
---|
307 | |
---|
308 | static proc farey_task(intvec range) |
---|
309 | { |
---|
310 | ideal result = farey(ideal(farey_arg[range[1]..range[2]]), farey_N); |
---|
311 | return(result); |
---|
312 | } |
---|
313 | |
---|
314 | static proc par_range(int N, list #) |
---|
315 | { |
---|
316 | int nchunks = 2*getcores(); |
---|
317 | if (nchunks > N) { |
---|
318 | nchunks = N; |
---|
319 | } |
---|
320 | if (size(#)) { |
---|
321 | int index = #[1]; |
---|
322 | intvec range = ((N*(index-1)) div nchunks)+1, (N*index) div nchunks; |
---|
323 | return(range); |
---|
324 | } |
---|
325 | else { |
---|
326 | return(nchunks); |
---|
327 | } |
---|
328 | } |
---|
329 | |
---|
330 | static proc NbModProcs() |
---|
331 | { |
---|
332 | int available = system("semaphore", "get_value", sem_cores)+1; |
---|
333 | int nb; |
---|
334 | if (available < 16) { |
---|
335 | nb = ((10 div available)+1-(10%available == 0))*available; |
---|
336 | } |
---|
337 | else { // gives approx. (log_2(available))^2 |
---|
338 | int tmp = available; |
---|
339 | while (tmp > 1) { |
---|
340 | tmp = tmp div 2; |
---|
341 | nb++; |
---|
342 | } // nb = log_2(available) |
---|
343 | nb = ((2*nb+1)*available) div (2^nb) + (nb-1)^2 - 2; |
---|
344 | } |
---|
345 | return(nb); |
---|
346 | } |
---|
347 | |
---|