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

spielwiese
Last change on this file since af7c47 was af7c47, checked in by Andreas Steenpass <steenpass@…>, 7 years ago
fix: sort the indices returned by deleteUnluckyPrimes()
  • Property mode set to 100644
File size: 10.6 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(modresults, primes);
174        }
175        else {
176            result_lift = chinrem(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 primeList(int n, int pmax)
223{
224    list primes;
225    int p = pmax;
226    int i;
227    for (i = 1; i <= n; i++) {
228        if (p < 2) {
229            ERROR("no more primes");
230        }
231        p = prime(p);
232        task t(i) = "Modular::primeList_task", list(p);
233        p--;
234    }
235    startTasks(t(1..n));
236    waitAllTasks(t(1..n));
237    int j;
238    for (i = 1; i <= n; i++) {
239        if (getResult(t(i))) {
240            j++;
241            primes[j] = getArguments(t(i))[1];
242        }
243        killTask(t(i));
244    }
245    if (j < n) {
246        primes = primes+primeList(n-j, p);
247    }
248    return(primes);
249}
250
251static proc primeList_task(int p)
252{
253    return(Modular::primeTest(p, Arguments));
254}
255
256static proc modp(int p)
257{
258    def br = basering;
259    list lbr = ringlist(br);
260    if (typeof(lbr[1]) == "int") {
261        lbr[1] = p;
262    }
263    else {
264        lbr[1][1] = p;
265    }
266    def rp = ring(lbr);
267    setring(rp);
268    list args = fetch(br, Arguments);
269    execute("def result = "+Command+"("+Tasks::argsToString("args", size(args))
270        +");");
271    setring(br);
272    def result = fetch(rp, result);
273    return(result);
274}
275
276static proc primeTest_default(int p, alias list args)
277{
278    return(1);
279}
280
281static proc deleteUnluckyPrimes_default(alias list modresults)
282{
283    return(list());
284}
285
286static proc pTest_default(string command, alias list args, def result, int p)
287{
288    return(1);
289}
290
291static proc finalTest_default(string command, alias list args, def result)
292{
293    return(1);
294}
295
296static proc farey_parallel(def farey_arg, bigint farey_N)
297{
298    arg_type = typeof(farey_arg);
299    if (arg_type != "bigint" && arg_type != "ideal"
300        && arg_type != "module" && arg_type != "matrix") {
301        ERROR("wrong input type");
302    }
303    if (arg_type == "bigint" || arg_type == "matrix") {
304        return(farey(farey_arg, farey_N));
305    }   // else: farey_arg is an ideal or a module
306    exportto(Modular, farey_arg);
307    exportto(Modular, farey_N);
308    int size_arg = ncols(farey_arg);
309    int chunks = par_range(size_arg);
310    intvec range;
311    int i;
312    for (i = chunks; i > 0; i--) {
313        range = par_range(size_arg, i);
314        task t(i) = "Modular::farey_task", list(range);
315    }
316    startTasks(t(1..chunks));
317    waitAllTasks(t(1..chunks));
318    def result = getResult(t(chunks));
319    for (i = chunks-1; i > 0; i--) {
320        result = getResult(t(i)), result;
321        killTask(t(i));
322    }
323    kill farey_arg;
324    kill farey_N;
325    return(result);
326}
327
328static proc farey_task(intvec range)
329{
330    execute("def result = farey("+arg_type+"(farey_arg[range[1]..range[2]]),"
331        +" farey_N);");
332    return(result);
333}
334
335static proc par_range(int N, list #)
336{
337    int nchunks = 2*getcores();
338    if (nchunks > N) {
339        nchunks = N;
340    }
341    if (size(#)) {
342        int index = #[1];
343        intvec range = ((N*(index-1)) div nchunks)+1, (N*index) div nchunks;
344        return(range);
345    }
346    else {
347        return(nchunks);
348    }
349}
350
351static proc NbModProcs()
352{
353    int available = system("semaphore", "get_value", sem_cores)+1;
354    int nb;
355    if (available < 16) {
356        nb = ((10 div available)+1-(10%available == 0))*available;
357    }
358    else {   // gives approx. (log_2(available))^2
359        int tmp = available;
360        while (tmp > 1) {
361            tmp = tmp div 2;
362            nb++;
363        }   // nb = log_2(available)
364        nb = ((2*nb+1)*available) div (2^nb) + (nb-1)^2 - 2;
365    }
366    return(nb);
367}
368
Note: See TracBrowser for help on using the repository browser.