source: git/Singular/LIB/modular.lib @ 0a374d

spielwiese
Last change on this file since 0a374d was 0a374d, checked in by Andreas Steenpass <steenpass@…>, 5 years ago
fix cases where system("semaphore", "get_value", ) is negative
  • Property mode set to 100644
File size: 12.1 KB
Line 
1////////////////////////////////////////////////////////////////////
2version="version modular.lib 4.1.2.0 Feb_2019 "; // $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    {
53        LIB "resources.lib";
54    }
55    int sem_cores = Resources::sem_cores;   // the number of processor cores
56    exportto(Modular, sem_cores);
57}
58
59proc modular(string Command, alias list Arguments, list #)
60"USAGE:   modular(command, arguments[, primeTest, deleteUnluckyPrimes, pTest,
61          finalTest, pmax), command string, arguments list, primeTest proc,
62          deleteUnluckyPrimes proc, pTest proc, finalTest proc, pmax int
63RETURN:   the result of @code{command} applied to @code{arguments},
64          computed using modular methods.
65NOTE:     For the general algorithm and the role of the optional arguments
66          primeTest, deleteUnluckyPrimes, pTest, and finalTest, see
67          @ref{modStd} and the reference given in @ref{modular_lib}. The
68          default for these arguments is that all tests succeed and that all
69          primes are assumed to be lucky.
70       @* The type of the result when @code{command} is applied to
71          @code{arguments} must be either @code{bigint}, @code{ideal},
72          @code{module}, or @code{matrix}.
73       @* The optional argument pmax is an upper bound for the prime numbers
74          to be used for the modular computations. The default is 2147483647
75          (largest prime which can be represented as an @code{int} in
76          Singular), or 536870909 (largest prime below 2^29} for baserings with
77          parameters.
78SEE ALSO: modStd
79EXAMPLE:  example modular; shows an example"
80{
81    /* check for errors */
82    if (char(basering) != 0)
83    { ERROR("The characteristic must be zero."); }
84
85    /* auxiliary variables */
86    int i;
87
88    /* set maximal prime number */
89    int pmax = 2147483647;
90    if (npars(basering) > 0)
91    {
92        pmax = 536870909;   // prime(2^29)
93    }
94
95    /* read optional parameters */
96    list defaults = list(primeTest_default, deleteUnluckyPrimes_default,
97        pTest_default, finalTest_default, pmax);
98    for (i = 1; i <= size(defaults); i++)
99    {
100        if (typeof(#[i]) != typeof(defaults[i]))
101        {
102            # = insert(#, defaults[i], i-1);
103        }
104    }
105    if (size(#) != size(defaults))
106    { ERROR("wrong optional parameters"); }
107    proc primeTest = #[1];
108    proc deleteUnluckyPrimes = #[2];
109    proc pTest = #[3];
110    proc finalTest = #[4];
111    pmax = #[5];
112    exportto(Modular, primeTest);
113
114    /* export command and arguments */
115    exportto(Modular, Command);
116    exportto(Modular, Arguments);
117
118    /* modular computations */
119    def result;
120    def result_lift;
121    bigint N = 1;
122    list modresults;
123    list primes;
124    int nAllPrimes;
125    int nNewPrimes;
126    int p;
127    list indices;
128    int ncores_available;
129    while (1)
130    {
131        // compute list of primes
132        if (nAllPrimes == 0)
133        {
134            nNewPrimes = NbModProcs();
135        }
136        else
137        {
138            ncores_available = max(1, system("semaphore", "get_value",
139                    sem_cores)+1);
140            if (nAllPrimes < ncores_available)
141            {
142                nNewPrimes = nAllPrimes;
143            }
144            else
145            {
146                nNewPrimes = (nAllPrimes div ncores_available)
147                    *ncores_available;
148            }
149        }
150        primes = primeList(nNewPrimes, pmax);
151        pmax = primes[size(primes)]-1;
152        nAllPrimes = nAllPrimes+nNewPrimes;
153
154        // do computation modulo several primes
155        for (i = size(primes); i > 0; i--)
156        {
157            task t(i) = "Modular::modp", primes[i];
158        }
159        startTasks(t(1..size(primes)));
160        waitAllTasks(t(1..size(primes)));
161        for (i = size(primes); i > 0; i--)
162        {
163            modresults[i] = getResult(t(i));
164            killTask(t(i));
165            kill t(i);
166        }
167
168        // delete unlucky primes
169        indices = deleteUnluckyPrimes(modresults);
170        indices = sort(indices)[1];
171        for (i = size(indices); i > 1; i--)
172        {
173            if (indices[i] == indices[i-1])
174            {
175                indices = delete(indices, i);
176            }
177        }
178        for (i = size(indices); i > 0; i--)
179        {
180            modresults = delete(modresults, indices[i]);
181            primes = delete(primes, indices[i]);
182        }
183
184        // lift result
185        if (N == 1)
186        {
187            result_lift = chinrem_recursive(modresults, primes);
188        }
189        else
190        {
191            result_lift = chinrem_recursive(list(result_lift)+modresults,
192                list(N)+primes);
193        }
194        modresults = list();
195        for (i = size(primes); i > 0; i--)
196        {
197            N = N*primes[i];
198        }
199
200        // apply farey
201        result = farey_parallel(result_lift, N);
202
203        // pTest
204        p = prime(random(pmax div 2, pmax-1));
205        while (!Modular::primeTest(p, Arguments))
206        {
207            if (p <= 2) { ERROR("no more primes"); }
208            p = prime(random(p div 2, p-1));
209        }
210        if (!pTest(Command, Arguments, result, p)) { continue; }
211
212        // finalTest
213        if (finalTest(Command, Arguments, result)) break;
214    }
215
216    /* kill exported data */
217    kill Command;
218    kill Arguments;
219    kill primeTest;
220
221    /* return of result */
222    return(result);
223}
224example
225{
226    "EXAMPLE:";
227    echo = 2;
228    ring R = 0, (x,y), dp;
229    ideal I = x9y2+x10, x2y7-y8;
230    modular("std", list(I));
231}
232
233static proc chinrem_recursive(list modresults, list moduli)
234{
235    if (typeof(modresults[1]) != "list")
236    {
237        return(chinrem(modresults, moduli));
238    }
239    // if typeof(modresults[1]) == "list", then we assume
240    // size(modresults[1]) == size(modresults[k])
241    // for k = 2, ..., size(modresults)
242    int n_sublists = size(modresults[1]);
243    int i, j;
244    list modresults_sublists;
245    for (j = n_sublists; j > 0; j--)
246    {
247        modresults_sublists[j] = list();
248        for (i = size(modresults); i > 0; i--)
249        {
250            modresults_sublists[j][i] = modresults[i][j];
251        }
252        task t(j) = "Modular::chinrem_recursive",
253            list(modresults_sublists[j], moduli);
254    }
255    startTasks(t(1..n_sublists));
256    waitAllTasks(t(1..n_sublists));
257    list result;
258    for (j = n_sublists; j > 0; j--)
259    {
260        result[j] = getResult(t(j));
261        killTask(t(j));
262    }
263    return(result);
264}
265
266static proc primeList(int n, int pmax)
267{
268    list primes;
269    int p = pmax;
270    int i;
271    for (i = 1; i <= n; i++)
272    {
273        if (p < 2)
274        { ERROR("no more primes"); }
275        p = prime(p);
276        task t(i) = "Modular::primeList_task", list(p);
277        p--;
278    }
279    startTasks(t(1..n));
280    waitAllTasks(t(1..n));
281    int j;
282    for (i = 1; i <= n; i++)
283    {
284        if (getResult(t(i)))
285        {
286            j++;
287            primes[j] = getArguments(t(i))[1];
288        }
289        killTask(t(i));
290    }
291    if (j < n)
292    {
293        primes = primes+primeList(n-j, p);
294    }
295    return(primes);
296}
297
298static proc primeList_task(int p)
299{
300    return(Modular::primeTest(p, Arguments));
301}
302
303static proc modp(int p)
304{
305    def br = basering;
306    list lbr = ringlist(br);
307    if (typeof(lbr[1]) == "int")
308    { lbr[1] = p; }
309    else
310    { lbr[1][1] = p; }
311    def rp = ring(lbr);
312    setring(rp);
313    list args = fetch(br, Arguments);
314    execute("def result = "+Command+"("+Tasks::argsToString("args", size(args))
315        +");");
316    setring(br);
317    def result = fetch(rp, result);
318    return(result);
319}
320
321static proc primeTest_default(int p, alias list args)
322{
323    return(1);
324}
325
326static proc deleteUnluckyPrimes_default(alias list modresults)
327{
328    return(list());
329}
330
331static proc pTest_default(string command, alias list args, alias def result,
332    int p)
333{
334    return(1);
335}
336
337static proc finalTest_default(string command, alias list args,
338    alias def result)
339{
340    return(1);
341}
342
343static proc farey_parallel(def farey_arg, bigint farey_N)
344{
345    arg_type = typeof(farey_arg);
346    if (arg_type != "bigint" && arg_type != "ideal" && arg_type != "module"
347        && arg_type != "matrix" && arg_type != "list")
348    { ERROR("wrong input type"); }
349    int i;
350    if (arg_type == "list")
351    {
352        int size_farey_arg = size(farey_arg);
353        for (i = size_farey_arg; i > 0; i--)
354        {
355            task t(i) = "Modular::farey_parallel", list(farey_arg[i], farey_N);
356        }
357        startTasks(t(1..size_farey_arg));
358        waitAllTasks(t(1..size_farey_arg));
359        list result;
360        for (i = size_farey_arg; i > 0; i--)
361        {
362            result[i] = getResult(t(i));
363            killTask(t(i));
364        }
365        return(result);
366    }
367    if (arg_type == "bigint" || arg_type == "matrix")
368    { return(farey(farey_arg, farey_N)); }
369    // else: farey_arg is an ideal or a module
370    exportto(Modular, farey_arg);
371    exportto(Modular, farey_N);
372    int size_arg = ncols(farey_arg);
373    int chunks = par_range(size_arg);
374    intvec range;
375    for (i = chunks; i > 0; i--)
376    {
377        range = par_range(size_arg, i);
378        task t(i) = "Modular::farey_task", list(range);
379    }
380    startTasks(t(1..chunks));
381    waitAllTasks(t(1..chunks));
382    def result = getResult(t(chunks));
383    for (i = chunks-1; i > 0; i--)
384    {
385        result = getResult(t(i)), result;
386        killTask(t(i));
387    }
388    kill farey_arg;
389    kill farey_N;
390    return(result);
391}
392
393static proc farey_task(intvec range)
394{
395    execute("def result = farey("+arg_type+"(farey_arg[range[1]..range[2]]),"
396        +" farey_N);");
397    return(result);
398}
399
400static proc par_range(int N, list #)
401{
402    int nchunks = 2*getcores();
403    if (nchunks > N) {
404        nchunks = N;
405    }
406    if (size(#)) {
407        int index = #[1];
408        intvec range = ((N*(index-1)) div nchunks)+1, (N*index) div nchunks;
409        return(range);
410    }
411    else {
412        return(nchunks);
413    }
414}
415
416static proc NbModProcs()
417{
418    int available = max(1, system("semaphore", "get_value", sem_cores)+1);
419    int nb;
420    if (available < 16) {
421        nb = ((10 div available)+1-(10%available == 0))*available;
422    }
423    else {   // gives approx. (log_2(available))^2
424        int tmp = available;
425        while (tmp > 1) {
426            tmp = tmp div 2;
427            nb++;
428        }   // nb = log_2(available)
429        nb = ((2*nb+1)*available) div (2^nb) + (nb-1)^2 - 2;
430    }
431    return(nb);
432}
433
Note: See TracBrowser for help on using the repository browser.