Skip to content

Commit 7ef6b91

Browse files
committed
v.0.9.10
* faster sparse polynomial division and multiplication using Maxheap * Abacus.Heap implementation * System of diophantine equations check additional rows for solution if over-determined system * update examples, tests
1 parent ecc08fe commit 7ef6b91

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+818
-727
lines changed

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ A variety of combinatorial algorithms, number theory algorithms & statistics
139139
* [Numerical algorithms for the computation of the Smith normal form of integral matrices, C. Koukouvinos, M. Mitrouli, J. Seberry](https://ro.uow.edu.au/cgi/viewcontent.cgi?referer=https://www.google.com/&httpsredir=1&article=2173&context=infopapers)
140140
* [Fraction-free matrix factors: new forms for LU and QR factors, Wenqin ZHOU, David J. JEFFREY](http://ftp.cecm.sfu.ca/personal/pborwein/MITACS/papers/FFMatFacs08.pdf)
141141
* [Algorithms and Data Structures for Sparse Polynomial Arithmetic, M. Asadi, A. Brandt, R. H. C. Moir, M. M. Maza](https://www.researchgate.net/publication/333182217_Algorithms_and_Data_Structures_for_Sparse_Polynomial_Arithmetic)
142-
* [High Performance Sparse Multivariate Polynomials: Fundamental Data Structures and Algorithms, Alex Brandt (MSc thesis)](https://pdfs.semanticscholar.org/0f93/5035aefc4afce591cd52c507cd7fa35eb061.pdf?_ga=2.149338422.1292196222.1579944113-1940599073.1579944113)
142+
* [High Performance Sparse Multivariate Polynomials: Fundamental Data Structures and Algorithms, Alex Brandt (MSc thesis)](https://www.semanticscholar.org/paper/High-Performance-Sparse-Multivariate-Polynomials%3A-Brandt/016a97690ecaed04d7a60c1dbf27eb5a96de2dc1)
143143
* [Algorithms for Normal Forms for Matrices of Polynomials and Ore Polynomials, Howard Cheng (PhD thesis)](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.9.4150&rep=rep1&type=pdf)
144144

145145
### Example API
@@ -605,7 +605,7 @@ o.dispose()
605605
* support efficient primality tests and prime sieves **[DONE PARTIALY]**
606606
* support efficient integer factorization algorithms **[DONE PARTIALY]**
607607
* support solutions of (systems of) **linear diophantine and linear congruence equations** (with one or many variables) **[DONE]**
608-
* use sparse representation for polynomials (univariate and multivariate) instead of the, in general, inefficient dense representation **[DONE PARTIALY]**
608+
* use sparse representation for polynomials (univariate and multivariate) instead of the, in general, inefficient dense representation (and optimise associated arithmetic operations) **[DONE]**
609609
* support (univariate) polynomial (partial) factorisation, (rational) root finding **[DONE PARTIALY]**
610610
* support multivariate polynomial, multivariate division/reduction, groebner basis computations (TODO)
611611
* implement `LLL` algorithm (TODO)

src/js/Abacus.js

+70-7
Original file line numberDiff line numberDiff line change
@@ -3325,6 +3325,12 @@ function solvediophs( a, b, with_param, with_free_vars )
33253325
}));
33263326
});
33273327
free_vars.symbol = symbol;
3328+
3329+
// if over-determined system (m > k)
3330+
// check if additional rows are satisfied by solution as well
3331+
for(i=k; i<m; i++)
3332+
if ( !Expr(solutions.map(function(xj){return xj.mul(a.val[i][j]);})).equ(b[i]) )
3333+
return null; // no solution
33283334

33293335
/*
33303336
// solve by successive substitution
@@ -4640,6 +4646,7 @@ function addition_sparse( a, b, do_subtraction, gt )
46404646
// merge terms by efficient merging and produce already sorted order c
46414647
// eg http://www.cecm.sfu.ca/~mmonagan/teaching/TopicsinCA11/johnson.pdf
46424648
// and https://www.researchgate.net/publication/333182217_Algorithms_and_Data_Structures_for_Sparse_Polynomial_Arithmetic
4649+
// and https://www.semanticscholar.org/paper/High-Performance-Sparse-Multivariate-Polynomials%3A-Brandt/016a97690ecaed04d7a60c1dbf27eb5a96de2dc1
46434650
gt = gt || function(a, b){return a>b;};
46444651
var i = 0, j = 0, k = 0, n1 = a.length, n2 = b.length, c = new Array(n1+n2), res;
46454652
while( i<n1 && j<n2 )
@@ -4681,7 +4688,8 @@ function multiplication_sparse( a, b )
46814688
// merge terms by efficient merging and produce already sorted order c
46824689
// eg http://www.cecm.sfu.ca/~mmonagan/teaching/TopicsinCA11/johnson.pdf
46834690
// and https://www.researchgate.net/publication/333182217_Algorithms_and_Data_Structures_for_Sparse_Polynomial_Arithmetic
4684-
var k, l, s, t, n1, n2, c, f, max, res, heap;
4691+
// and https://www.semanticscholar.org/paper/High-Performance-Sparse-Multivariate-Polynomials%3A-Brandt/016a97690ecaed04d7a60c1dbf27eb5a96de2dc1
4692+
var k, t, n1, n2, c, f, max, heap;
46854693
if ( a.length > b.length ){ t=a; a=b; b=t;} // swap to achieve better performance
46864694
n1 = a.length; n2 = b.length; c = new Array(n1*n2);
46874695
if ( 0<n1 && 0<n2 )
@@ -4691,17 +4699,22 @@ function multiplication_sparse( a, b )
46914699
heap = Heap(array(n1, function(i){
46924700
return [a[i].e+b[0].e, a[i].c.mul(b[0].c), i];
46934701
}), "max", function(a, b){
4694-
return a[0]<b[0] ? -1 : (a[0]>b[0] ? 1 : 0);
4702+
return a[0]-b[0];
46954703
});
46964704
f = array(n1, 0);
46974705
while( max=heap.peek() )
46984706
{
4699-
if ( c[k].e!==max[0] && !c[k].c.equ(0) ) c[++k] = Coeff(0, max[0]);
4700-
heap.pop();
4707+
if ( c[k].e!==max[0] )
4708+
{
4709+
if ( !c[k].c.equ(0) ) c[++k] = Coeff(0, -1);
4710+
c[k].e = max[0];
4711+
}
47014712
c[k].c = c[k].c.add(max[1]);
47024713
f[max[2]]++;
4703-
if ( f[max[2]] < n2 ) heap.push([a[max[2]].e+b[f[max[2]]].e, a[max[2]].c.mul(b[f[max[2]]].c), max[2]]);
4714+
if ( f[max[2]] < n2 ) heap.replace([a[max[2]].e+b[f[max[2]]].e, a[max[2]].c.mul(b[f[max[2]]].c), max[2]]);
4715+
else heap.pop();
47044716
}
4717+
heap.dispose();
47054718
if ( c.length > k+1 ) c.length = k+1; // truncate if needed
47064719
}
47074720
return c;
@@ -8495,7 +8508,7 @@ Polynomial = Abacus.Polynomial = Class(INumber, {
84958508
,div: function( x, q_and_r ) {
84968509
var self = this, Arithmetic = Abacus.Arithmetic,
84978510
O = Arithmetic.O, I = Arithmetic.I,
8498-
q, r, d, diff, diff0;
8511+
q, r, d, diff, diff0, k, res, Q, a, na, b, nb, heap;
84998512

85008513
if ( x instanceof Complex ) x = x.real;
85018514
else if ( Arithmetic.isNumber(x) ) x = Rational(x);
@@ -8518,9 +8531,10 @@ Polynomial = Abacus.Polynomial = Class(INumber, {
85188531
}), self.symbol);
85198532
return true===q_and_r ? [q, Polynomial([], self.symbol)] : q;
85208533
}
8534+
85218535
// polynomial long division
85228536
// TODO: make it faster
8523-
r = Polynomial(self);
8537+
/*r = Polynomial(self);
85248538
diff = r.deg()-x.deg();
85258539
if ( 0 <= diff )
85268540
{
@@ -8539,7 +8553,56 @@ Polynomial = Abacus.Polynomial = Class(INumber, {
85398553
{
85408554
q = [];
85418555
}
8556+
q = Polynomial(q, self.symbol);*/
8557+
8558+
// sparse polynomial long division
8559+
// https://www.semanticscholar.org/paper/High-Performance-Sparse-Multivariate-Polynomials%3A-Brandt/016a97690ecaed04d7a60c1dbf27eb5a96de2dc1
8560+
a = self.coeff; na = a.length; b = x.coeff; nb = b.length;
8561+
heap = Heap([], "max", function(a,b){return a.coeff.e-b.coeff.e;});
8562+
q = []; r = []; k = 0;
8563+
while( (d=heap.peek()) || k<na )
8564+
{
8565+
if ( (null == d) || (k<na && d.coeff.e<a[k].e) )
8566+
{
8567+
res = a[k].clone();
8568+
k++;
8569+
}
8570+
else if ( k<na && d.coeff.e===a[k].e )
8571+
{
8572+
res = Coeff(a[k].c.sub(d.coeff.c), d.coeff.e);
8573+
if ( nb>d.n )
8574+
heap.replace({coeff:Coeff(d.Q.c.mul(b[d.n].c), d.Q.e+b[d.n].e), n:d.n+1, Q:d.Q});
8575+
else
8576+
heap.pop();
8577+
k++;
8578+
8579+
if ( res.c.equ(O) ) continue; // zero coefficient, skip
8580+
}
8581+
else
8582+
{
8583+
res = Coeff(d.coeff.c.neg(), d.coeff.e);
8584+
if ( nb>d.n )
8585+
heap.replace({coeff:Coeff(d.Q.c.mul(b[d.n].c), d.Q.e+b[d.n].e), n:d.n+1, Q:d.Q});
8586+
else
8587+
heap.pop();
8588+
}
8589+
8590+
if ( b[0].e<=res.e ) // if b[0] divides res
8591+
{
8592+
Q = Coeff(res.c.div(b[0].c), res.e-b[0].e);
8593+
q = addition_sparse(q, [Q]);
8594+
if ( nb>1 )
8595+
heap.push({coeff:Coeff(Q.c.mul(b[1].c), Q.e+b[1].e), n:2, Q:Q});
8596+
}
8597+
else
8598+
{
8599+
r = addition_sparse(r, [res]);
8600+
}
8601+
}
8602+
heap.dispose();
85428603
q = Polynomial(q, self.symbol);
8604+
r = Polynomial(r, self.symbol);
8605+
85438606
// return both quotient and remainder if requested
85448607
return true===q_and_r ? [q, r] : q;
85458608
}

src/js/Abacus.min.js

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/combination_subsets.txt

+24-24
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Abacus.Subsets via Abacus.Combinations (VERSION = 0.9.9)
1+
Abacus.Subsets via Abacus.Combinations (VERSION = 0.9.10)
22
---
33
o = Abacus.CombinatorialIterator([Abacus.Combination(5,0),Abacus.Combination(5,1),Abacus.Combination(5,2),Abacus.Combination(5,3),Abacus.Combination(5,4),Abacus.Combination(5,5)])
44
o.total()
@@ -243,38 +243,38 @@ o.order("colex,reversed")
243243
[ 0 ]
244244
[]
245245
o.order("random")
246-
[ 1 ]
247-
[ 0, 2 ]
248-
[ 2, 1 ]
249-
[ 0 ]
246+
[ 0, 1, 2, 3 ]
247+
[ 4 ]
248+
[ 2, 3, 4 ]
249+
[ 1, 3, 4 ]
250+
[ 0, 1, 4 ]
251+
[ 3 ]
250252
[ 0, 1, 2 ]
251253
[ 1 ]
252254
[ 3, 1 ]
253-
[ 1, 4 ]
254-
[ 1, 3 ]
255-
[ 0, 1, 2, 4 ]
256-
[ 1, 0 ]
257-
[ 2 ]
258-
[ 2 ]
255+
[ 3, 1 ]
256+
[ 1, 2, 3, 4 ]
259257
[ 0, 1, 4 ]
260-
[ 0, 1, 3, 4 ]
258+
[ 1, 2 ]
259+
[ 4 ]
260+
[ 0, 3 ]
261+
[ 1, 2 ]
261262
[ 1, 2, 4 ]
262-
[ 0, 1, 2, 3 ]
263263
[ 0, 2, 3, 4 ]
264-
[ 1 ]
265-
[ 1, 2 ]
266-
[ 2, 0 ]
267-
[ 1, 3, 4 ]
268-
[ 0, 3, 4 ]
264+
[ 3 ]
265+
[ 0, 1, 2, 3, 4 ]
269266
[ 0, 2, 3, 4 ]
270267
[ 1, 3 ]
268+
[ 2, 3 ]
269+
[ 1, 2 ]
271270
[ 0, 1, 3, 4 ]
272-
[ 0, 1 ]
273-
[ 2, 3, 4 ]
274-
[ 0, 1, 2, 4 ]
275-
[ 4, 3 ]
276-
[ 1 ]
277-
[ 0, 1, 2, 4 ]
271+
[ 1, 3, 4 ]
272+
[ 2 ]
273+
[ 1, 2, 3, 4 ]
274+
[ 0, 1, 2, 3, 4 ]
275+
[ 0, 2, 3, 4 ]
276+
[ 0, 2, 3, 4 ]
277+
[ 0, 2, 3, 4 ]
278278
o.random()
279279
[ 3 ]
280280
o.dispose()

test/combinations.txt

+13-13
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Abacus.Combinations (VERSION = 0.9.9)
1+
Abacus.Combinations (VERSION = 0.9.10)
22
---
33
o = Abacus.Combination(6,3)
44
o.total()
@@ -159,28 +159,28 @@ o.order("colex,reversed")
159159
[ 0, 1, 3 ]
160160
[ 0, 1, 2 ]
161161
o.order("random")
162+
[ 0, 2, 5 ]
162163
[ 1, 4, 5 ]
163-
[ 0, 2, 4 ]
164164
[ 2, 3, 4 ]
165-
[ 1, 2, 5 ]
166-
[ 0, 2, 3 ]
167-
[ 0, 2, 5 ]
168-
[ 0, 3, 4 ]
169-
[ 0, 3, 5 ]
170-
[ 2, 3, 5 ]
171-
[ 0, 1, 4 ]
172-
[ 1, 2, 3 ]
173-
[ 0, 1, 5 ]
174165
[ 0, 4, 5 ]
166+
[ 0, 2, 4 ]
167+
[ 1, 3, 5 ]
175168
[ 2, 4, 5 ]
169+
[ 2, 3, 5 ]
176170
[ 0, 1, 3 ]
177-
[ 1, 3, 5 ]
178171
[ 1, 3, 4 ]
172+
[ 0, 3, 5 ]
179173
[ 1, 2, 4 ]
174+
[ 0, 3, 4 ]
175+
[ 0, 1, 5 ]
176+
[ 1, 2, 5 ]
180177
[ 0, 1, 2 ]
181178
[ 3, 4, 5 ]
179+
[ 0, 2, 3 ]
180+
[ 0, 1, 4 ]
181+
[ 1, 2, 3 ]
182182
o.random()
183-
[ 2, 3, 4 ]
183+
[ 0, 1, 3 ]
184184
o.order("colex").range(-5, -1)
185185
[ 2, 3, 5 ]
186186
[ 0, 4, 5 ]

test/combinations_repeats.txt

+44-44
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Abacus.CombinationRepeats (VERSION = 0.9.9)
1+
Abacus.CombinationRepeats (VERSION = 0.9.10)
22
---
33
o = Abacus.Combination(6,3,{type:"repeated"})
44
o.total()
@@ -411,64 +411,64 @@ o.order("colex,reversed")
411411
[ 0, 0, 1 ]
412412
[ 0, 0, 0 ]
413413
o.order("random")
414-
[ 2, 3, 5 ]
415-
[ 2, 3, 3 ]
416-
[ 3, 3, 4 ]
417-
[ 4, 5, 5 ]
418-
[ 2, 2, 3 ]
419-
[ 2, 3, 4 ]
414+
[ 0, 0, 3 ]
420415
[ 1, 1, 5 ]
421-
[ 0, 3, 3 ]
422-
[ 1, 1, 1 ]
423-
[ 0, 0, 4 ]
424-
[ 2, 4, 5 ]
425-
[ 1, 3, 4 ]
426-
[ 1, 2, 2 ]
427416
[ 2, 2, 5 ]
428-
[ 0, 3, 4 ]
417+
[ 2, 3, 3 ]
418+
[ 0, 1, 1 ]
419+
[ 0, 2, 2 ]
420+
[ 0, 1, 5 ]
421+
[ 4, 4, 4 ]
422+
[ 0, 1, 4 ]
423+
[ 0, 1, 2 ]
424+
[ 3, 5, 5 ]
425+
[ 4, 4, 5 ]
426+
[ 0, 5, 5 ]
427+
[ 2, 3, 5 ]
428+
[ 0, 3, 3 ]
429429
[ 0, 2, 3 ]
430-
[ 0, 0, 5 ]
431-
[ 1, 2, 4 ]
430+
[ 3, 3, 5 ]
432431
[ 2, 4, 4 ]
432+
[ 3, 4, 5 ]
433+
[ 0, 0, 2 ]
433434
[ 2, 2, 2 ]
434-
[ 3, 3, 5 ]
435-
[ 0, 2, 2 ]
435+
[ 0, 2, 4 ]
436436
[ 1, 2, 3 ]
437-
[ 0, 0, 3 ]
438-
[ 0, 4, 5 ]
437+
[ 1, 5, 5 ]
438+
[ 0, 0, 0 ]
439439
[ 1, 4, 5 ]
440-
[ 1, 1, 4 ]
440+
[ 1, 2, 4 ]
441+
[ 2, 2, 3 ]
441442
[ 0, 1, 3 ]
442-
[ 0, 1, 1 ]
443-
[ 1, 1, 3 ]
443+
[ 3, 4, 4 ]
444+
[ 1, 1, 4 ]
445+
[ 0, 0, 4 ]
446+
[ 0, 2, 5 ]
444447
[ 2, 2, 4 ]
448+
[ 1, 2, 2 ]
449+
[ 5, 5, 5 ]
450+
[ 2, 3, 4 ]
451+
[ 0, 0, 5 ]
452+
[ 3, 3, 4 ]
453+
[ 0, 3, 4 ]
445454
[ 0, 0, 1 ]
455+
[ 0, 3, 5 ]
456+
[ 1, 4, 4 ]
457+
[ 1, 3, 3 ]
458+
[ 4, 5, 5 ]
459+
[ 0, 4, 4 ]
460+
[ 1, 1, 3 ]
461+
[ 0, 4, 5 ]
462+
[ 1, 1, 1 ]
463+
[ 3, 3, 3 ]
446464
[ 1, 2, 5 ]
465+
[ 1, 3, 4 ]
447466
[ 2, 5, 5 ]
448-
[ 3, 4, 5 ]
449-
[ 3, 4, 4 ]
450-
[ 3, 3, 3 ]
451-
[ 1, 3, 3 ]
452-
[ 0, 1, 5 ]
453-
[ 1, 5, 5 ]
467+
[ 2, 4, 5 ]
454468
[ 1, 1, 2 ]
455-
[ 3, 5, 5 ]
456-
[ 0, 5, 5 ]
457-
[ 4, 4, 4 ]
458-
[ 1, 4, 4 ]
459-
[ 4, 4, 5 ]
460-
[ 0, 2, 5 ]
461-
[ 0, 0, 2 ]
462-
[ 0, 1, 4 ]
463469
[ 1, 3, 5 ]
464-
[ 0, 4, 4 ]
465-
[ 5, 5, 5 ]
466-
[ 0, 1, 2 ]
467-
[ 0, 0, 0 ]
468-
[ 0, 3, 5 ]
469-
[ 0, 2, 4 ]
470470
o.random()
471-
[ 4, 4, 4 ]
471+
[ 0, 3, 3 ]
472472
o.order("colex").range(-5, -1)
473473
[ 1, 5, 5 ]
474474
[ 2, 5, 5 ]

test/complex.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Abacus.Complex (VERSION = 0.9.9)
1+
Abacus.Complex (VERSION = 0.9.10)
22
---
33
o=Abacus.Complex()
44
o.toString()

0 commit comments

Comments
 (0)