source: git/Singular/LIB/modular.lib @ a88bbe

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