Skip to content

Commit

Permalink
v.0.9.3
Browse files Browse the repository at this point in the history
* fix partial tensor missing data when in reversed order due to wrong index computation
* take into account position of value in generating partial combinatorial tensor for strictly increasing or decreasing sequences (for example usefull in generating conditional combinations, still filtering is needed)
* Filter.SORTED can accept also <,>,=<,>= etc operands symbolising ordering
* update Readme, tests
  • Loading branch information
foo123 committed May 20, 2019
1 parent a5d2d04 commit 96271ff
Show file tree
Hide file tree
Showing 38 changed files with 909 additions and 761 deletions.
33 changes: 17 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ A combinatorics library for Node.js / Browser / XPCOM Javascript, PHP, Python, C
(php/python/java/c implementations in progress)


**version 0.9.2** (~ 69kB minified, ~ 22kB zipped)
**version 0.9.3** (~ 74kB minified, ~ 23kB zipped)

![abacus combinatorial numbers](/abacus.jpg)

Expand Down Expand Up @@ -75,9 +75,9 @@ A combinatorics library for Node.js / Browser / XPCOM Javascript, PHP, Python, C
* `LatinSquare` (`test/latin_squares.js`)
* `MagicSquare` (`test/magic_squares.js`)
* **algebraic composition** (of **fixed** dimensions at present) and **sequences** of combinatorial objects to construct new combinatorial objects (eg `all combinations` = `all permutations` **OF** `all unique combinations`, see `test/permutations_of_combinations.js` and `test/permutations_of_permutations.js`, `k-Derangements` = `(n,k) Combinations` **combined With** `(n-k) Derangements`, see `test/k-derangements.js` or `all subsets` = `(n,0)Combinations + (n,1)Combinations + .. + (n,n-1)Combinations + (n,n)Combinations`, see `test/combination_subsets.js`)
* custom (user-supplied callable) and/or built-in **filters** which can select and generate any custom and complex combinatorial object from filtering other combinatorial objects as efficiently as possible (e.g see `test/filtered.js`, `test/filtered_partitions.js`). Also **algebraic / boolean composition of filters** (i.e `.NOT()`, `.AND()`, `.OR()` and so on..)
* custom (user-supplied callable) and/or built-in **filters** which can select and generate any custom and complex combinatorial object from filtering other combinatorial objects as efficiently as possible (e.g see `test/filtered.js`, `test/filtered_partitions.js`). Also **algebraic / boolean composition of filters** (i.e `.NOT()`, `.AND()`, `.OR()` and so on..). **Note** that filtering should be **used with caution and only if no other method is currently possible** to generate the desired combinatorial object as **filtering is equivalent to exhaustive search** over the space of the original combinatorial object and as such can be an inefficient way to generate a combinatorial object (e.g see `test/filtered.js`). **Note2** with filtering applied some methods like `.total()`, `.hasNext()`, `.base()`, `.dimension()` still return data of the original object **not** the filtered object since that would require to pre-generate all the data and filter them afterwards instead of doing it one-by-one on each generation and would be impractical and unachievable for very large combinatorial objects, so be careful when using, for example, `.total()` with fitering applied
* **multiple (combined) iterator orderings &amp; traversals**: `lex`, `colex`, `random`, `reversed`, `reflected`, `minimal` (not implemented yet). For example: `"revlex"` (equivalent to `"lex,reversed"`), `"refcolex"` (equivalent to `"colex,reflected"`), and so on..
* **arbitrary range** of combinatorial objects in a number of supported orderings (ie `lex`, `colex`, `random`,..). **Note** `rank`/`unrank` have to be implemented for this feature to work
* **arbitrary range** of combinatorial objects in a number of supported orderings (ie `lex`, `colex`, `random`,..) (and with filtering applied, if set). **Note** `rank`/`unrank` methods have to be implemented for this feature to work
* **efficient and unbiased generation, (un)ranking, succession &amp; random methods** for supported combinatorial objects (see below)
* `big-integer arithmetic`, `PRNG`s and other `math` utilities can be **dynamicaly pluggable using external implementations**, making the lib very flexible especialy with respect to handling big-integers &amp; (pseudo-)random number generators

Expand Down Expand Up @@ -562,20 +562,21 @@ o.dispose()

### Todo

* apply built-in language `iterator`/`iterable` patterns (e.g ES6 `iterator` protocol, Python `__iter__` interface, PHP `Iterator` interface, ..). Combinatorial objects additionaly support a `doubly-linked list`-like interface, i.e `prev`/`next` accessors [DONE]
* support `biginteger` combinatorial computations e.g large factorials [DONE, the lib **does not support** biginteger arithmetic, but arithmetic routines have been made **dynamicaly pluggable** and one can use an external implementation to support combinatorics with bigintegers where needed as needed, see test examples for an example]
* support **multiple combined custom iterator orderings**, i.e `lex`, `colex`, `reversed`, `reflected`, `random` seamlessly and uniformly, both forward and backward [DONE, `random` ordering may be optimised further]
* support **efficient successor methods** (preferably `CAT/Loopless` methods) to generate next/prev object from current object [DONE]
* support **efficient ranking / unranking algorithms** and associated methods (preferably of `O(n)` or `O(nlgn)` complexity) for supported orderings [DONE]
* support multiple combinatorial orderings (ie `lex`, `colex`, `reflex`, `refcolex`, `minimal`, ..) **directly in the successor methods** instead of using post-transformations on object [DONE]
* support **unique and uniform random ordering traversals** for all combinatorial objects, so that the space of a combinatorial object can be traversed in **any random ordering uniquely and unbiasedly** (useful in some applications, eg backtracking) [DONE, see reference, used as custom iterator ordering, see above, may be optimised further]
* make sure the `.random` methods **uniformly and unbiasedly sample the combinatorial object space** (methods use unbiased sampling algorithms, however results in certain cases might depend on [quality of PRNGs](http://www0.cs.ucl.ac.uk/staff/d.jones/GoodPracticeRNG.pdf)) [DONE]
* support algebraic composition/cascading of combinatorial objects (of **fixed** dimensions at present) to construct new combinatorial objects (eg `all combinations` = `all permutations` **OF** `all unique combinations`) [DONE]
* support generation of supported combinatorial objects with additional **user-defined patterns/templates of constraints** to satisfy e.g *"only combinatorial objects matching `'(n)(m)(1){2}(){3}(0)((n+1))((n+m)){4}'`"* pattern.. [DONE]
* add `LatinSquare`, `MagicSquare` algorithms [DONE]
* apply built-in language `iterator`/`iterable` patterns (e.g ES6 `iterator` protocol, Python `__iter__` interface, PHP `Iterator` interface, ..). Combinatorial objects additionaly support a `doubly-linked list`-like interface, i.e `prev`/`next` accessors **[DONE]**
* support `biginteger` combinatorial computations e.g large factorials **[DONE]**, the lib **does not support** biginteger arithmetic, but arithmetic routines have been made **dynamicaly pluggable** and one can use an external implementation to support combinatorics with bigintegers where needed as needed, see test examples for an example
* support **multiple combined custom iterator orderings**, i.e `lex`, `colex`, `reversed`, `reflected`, `random` seamlessly and uniformly, both forward and backward **[DONE, `random` ordering may be optimised further]**
* support **efficient successor methods** (preferably `CAT/Loopless` methods) to generate next/prev object from current object **[DONE]**
* support **efficient ranking / unranking algorithms** and associated methods (preferably of `O(n)` or `O(nlgn)` complexity) for supported orderings **[DONE]**
* support multiple combinatorial orderings (ie `lex`, `colex`, `reflex`, `refcolex`, `minimal`, ..) **directly in the successor methods** instead of using post-transformations on object **[DONE]**
* support **unique and uniform random ordering traversals** for all combinatorial objects, so that the space of a combinatorial object can be traversed in **any random ordering uniquely and unbiasedly** (useful in some applications, eg backtracking) **[DONE, see reference, used as custom iterator ordering, see above, may be optimised further]**
* make sure the `.random` methods **uniformly and unbiasedly sample the combinatorial object space** (methods use unbiased sampling algorithms, however results in certain cases might depend on [quality of PRNGs](http://www0.cs.ucl.ac.uk/staff/d.jones/GoodPracticeRNG.pdf)) **[DONE]**
* support algebraic composition/cascading of combinatorial objects (of **fixed** dimensions at present) to construct new combinatorial objects (eg `all combinations` = `all permutations` **OF** `all unique combinations`) **[DONE]**
* support generation of supported combinatorial objects with additional **user-defined patterns/templates of constraints** to satisfy e.g *"only combinatorial objects matching `'(n)(m)(1){2}(){3}(0)((n+1))((n+m)){4}'`"* pattern.. **[DONE]**
* add `LatinSquare`, `MagicSquare` algorithms **[DONE]**
* add run-time/lazy custom and/or built-in filtering support (with support for filter composition as well) to generate and select custom and complex combinatorial objects from filtering other combinatorial objects as efficiently as possible **[DONE]**
* add efficient `rank`/`unrank` methods for `DerangementPermutation`, `InvolutionPermutation`, `ConnectedPermutation`, `Composition` &amp; `Partition` (TODO)
* full support for `colex` ordering `Composition` &amp; `Partition` [DONE PARTIALY]
* full support for `colex` ordering `Composition` &amp; `Partition` **[DONE PARTIALY]**
* support `minimal`/`gray` ordering (and successor) for all supported combinatorial objects (TODO)
* use numeric arrays (ie `Uint32`) to store combinatorial items and/or make faster `successor` methods and other numerical routines to `asm.js` (TODO?)
* support generation (and counting) of combinatorial objects (including the basic supported ones) based on **generic user-defined symbolic constraints / symmetries / rules** to satisfy, for example `permutations` defined symbolicaly and directly by their *symmetries / constraints* instead of being hardcoded as elementary objects (TODO?)
* support generation (and counting) of combinatorial objects (including the basic supported ones) based on **generic user-defined symbolic constraints / symmetries / rules** to satisfy, for example `permutations` defined symbolicaly and directly by their *symmetries / constraints* instead of being hardcoded as elementary objects (TODO?, see using `filtering` as a similar alternative to this approach)
* support *graph-based* combinatorial objects like `Graph`, `Grammar`,.. (TODO?) (for regular grammars and expressions see [RegexAnalyzer](https://github.com/foo123/RegexAnalyzer) for an example)
111 changes: 93 additions & 18 deletions src/js/Abacus.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
*
* Abacus
* A combinatorics library for Node.js / Browser / XPCOM Javascript, PHP, Python, Java, C/C++
* @version: 0.9.2
* @version: 0.9.3
* https://github.com/foo123/Abacus
**/
!function( root, name, factory ){
Expand All @@ -22,7 +22,7 @@ else if ( !(name in root) ) /* Browser/WebWorker/.. */
/* module factory */ function ModuleFactory__Abacus( undef ){
"use strict";

var Abacus = {VERSION: "0.9.2"}, stdMath = Math, PROTO = 'prototype', CLASS = 'constructor'
var Abacus = {VERSION: "0.9.3"}, stdMath = Math, PROTO = 'prototype', CLASS = 'constructor'
,slice = Array.prototype.slice, HAS = Object[PROTO].hasOwnProperty, toString = Object[PROTO].toString
,log2 = stdMath.log2 || function(x) { return stdMath.log(x) / stdMath.LN2; }
,trim_re = /^\s+|\s+$/g
Expand Down Expand Up @@ -1543,6 +1543,7 @@ function conditional_combinatorial_tensor( v, value_conditions, extra_conditions
function gen_combinatorial_data( n, data, pos, value_conditions, options )
{
options = options || {};
pos = pos || array(data.length||0, 0, 1);
// conditions: ALGEBRAIC(STRING EXPR) AND/OR BOOLEAN(POSITIVE / NEGATIVE) => [values] per position
var min = null==options.min ? 0 : options.min,
max = null==options.max ? n-1 : options.max,
Expand Down Expand Up @@ -1692,9 +1693,27 @@ function gen_combinatorial_data( n, data, pos, value_conditions, options )

// check additional conditions
additional_conditions = is_callable(options.extra_conditions) ? function(v,i0,i1){
return (min<=v[i0] && v[i0]<=max) && options.extra_conditions(v,i0,i1);
var v0 = v[i0];
if (
// check in range
(min>v0 || v0>max) ||
// when strictly increasing sequence then value at pos i cannot be less than i since it has to accomodate the rest values as well before it, complementary for strictly decreasing sequence (for strictly decreasing sequence we do not know the number of elements that come after unlike for strictly increasing sequence where we can know, but as a workaround we can add last possible position in conditions with all possible values simply as a hint/clue on what is last possible position)
// (assume values in range 0..n-1 for positions 0..n-1 or reverse)
(V_INC === value_conditions && pos[i0]>v0) ||
(V_DEC === value_conditions && pos[pos.length-1]-pos[i0]>v0)
) return false
return options.extra_conditions(v,i0,i1);
} : function(v,i0,i1){
return (min<=v[i0] && v[i0]<=max);
var v0 = v[i0];
if (
// check in range
(min>v0 || v0>max) ||
// when strictly increasing sequence then value at pos i cannot be less than i since it has to accomodate the rest values as well before it, complementary for strictly decreasing sequence (for strictly decreasing sequence we do not know the number of elements that come after unlike for strictly increasing sequence where we can know, but as a workaround we can add last possible position in conditions with all possible values simply as a hint/clue on what is last possible position)
// (assume values in range 0..n-1 for positions 0..n-1 or reverse)
(V_INC === value_conditions && pos[i0]>v0) ||
(V_DEC === value_conditions && pos[pos.length-1]-pos[i0]>v0)
) return false
return true;
};

// compute valid combinatorial data satisfying conditions
Expand Down Expand Up @@ -2337,6 +2356,33 @@ function is_latin( square )
}
return M;
}*/
function find( a, b, nested )
{
if ( nested )
{
if ( !a || !a.length ) return -1;
var index, found, i, j, k, n = a.length, m = b.length;
for(i=0; i<n; i++)
{
k = a[i];
found = true;
for(j=0; j<m; j++)
{
if ( b[j] !== k[j] )
{
found = false;
break;
}
}
if ( found ) return i;
}
return -1;
}
else
{
return a && a.length ? a.indexOf(b) : -1;
}
}

// Abacus.Filter, Filter class used to define and combine filters to filter combinatorial object by them
Filter = Abacus.Filter = Class({
Expand All @@ -2360,8 +2406,32 @@ Filter = Abacus.Filter = Class({
});
}
,SORTED: function( dir, strict ) {
dir = -1 === dir ? -1 : 1;
if ( 2 > arguments.length || null == strict ) strict = true;
if ( is_string(dir) )
{
if ( "<" === dir )
{
dir = 1;
strict = true;
}
else if ( ">" === dir )
{
dir = -1;
strict = true;
}
else if ( "<=" === dir || "=<" === dir )
{
dir = 1;
strict = false;
}
else if ( ">=" === dir || "=>" === dir )
{
dir = -1;
strict = false;
}
}
dir = +dir;
dir = -1 === dir ? -1 : 1;
return Filter(-1 === dir ? function(item){
for(var item0=item[0],i=1,n=item.length; i<n; i++)
{
Expand Down Expand Up @@ -2408,9 +2478,9 @@ Filter = Abacus.Filter = Class({
}
,VAL: function( pos, val, comp ) {
comp = comp || "==";
val = +val;
//val = +val;
pos = +pos;
if ( ">=" === comp )
if ( ">=" === comp || "=>" === comp )
{
return Filter(function(item){ return 0<=pos && pos<item.length && item[pos]>=val; });
}
Expand All @@ -2422,7 +2492,7 @@ Filter = Abacus.Filter = Class({
{
return Filter(function(item){ return 0<=pos && pos<item.length && item[pos]<val; });
}
else if ( "<=" === comp )
else if ( "<=" === comp || "=<" === comp )
{
return Filter(function(item){ return 0<=pos && pos<item.length && item[pos]<=val; });
}
Expand All @@ -2438,7 +2508,7 @@ Filter = Abacus.Filter = Class({
,MAX: function( val, comp ) {
comp = comp || "==";
val = +val;
if ( ">=" === comp )
if ( ">=" === comp || "=>" === comp )
{
return Filter(function(item){ return operate(function(M,i){
if ( item[i] > M ) M = item[i];
Expand All @@ -2459,7 +2529,7 @@ Filter = Abacus.Filter = Class({
return M;
}, -Infinity, null, 0, item.length-1, 1) < val; });
}
else if ( "<=" === comp )
else if ( "<=" === comp || "=<" === comp )
{
return Filter(function(item){ return operate(function(M,i){
if ( item[i] > M ) M = item[i];
Expand All @@ -2484,7 +2554,7 @@ Filter = Abacus.Filter = Class({
,MIN: function( val, comp ) {
comp = comp || "==";
val = +val;
if ( ">=" === comp )
if ( ">=" === comp || "=>" === comp )
{
return Filter(function(item){ return operate(function(M,i){
if ( item[i] < M ) M = item[i];
Expand All @@ -2505,7 +2575,7 @@ Filter = Abacus.Filter = Class({
return M;
}, Infinity, null, 0, item.length-1, 1) < val; });
}
else if ( "<=" === comp )
else if ( "<=" === comp || "=<" === comp )
{
return Filter(function(item){ return operate(function(M,i){
if ( item[i] < M ) M = item[i];
Expand Down Expand Up @@ -3600,7 +3670,6 @@ CombinatorialIterator = Abacus.CombinatorialIterator = Class({
else self._next = has_next;
}
}while($.filter && current && !$.filter.apply(current, self)); // if custom filter, reject if invalid, try next

return current;
}

Expand Down Expand Up @@ -3830,14 +3899,20 @@ Tensor = Abacus.Tensor = Class(CombinatorialIterator, {
,succ: function( item, index, n, $, dir, TI ) {
if ( !n || (null == item) ) return null;
var type = $ && $.type ? $.type : "tensor",
order = $ && null!=$.order ? $.order : LEX;
order = $ && null!=$.order ? $.order : LEX,
Arithmetic = Abacus.Arithmetic, ind;
dir = -1 === dir ? -1 : 1;
if ( "partial" === type )
{
if ( !$.data || !$.data.length ) return null;
if ( REVERSED & order ) dir = -dir;
var i = null == index ? $.data.indexOf(item) : Abacus.Arithmetic.val(index);
return 0>dir ? (0<=i-1 ? $.data[i-1] : null) : (0<=i && i+1<$.data.length ? $.data[i+1] : null);
if ( REVERSED & order )
{
dir = -dir;
if ( null != index ) index = Arithmetic.sub(Arithmetic.N($.data.length-1),index);
}
if ( null == index ) index = find($.data, item, true);
ind = Arithmetic.val(index);
return 0>dir ? (0<=ind-1 ? $.data[ind-1] : null) : (0<=ind && ind+1<$.data.length ? $.data[ind+1] : null);
}
return !n[0] || (0 >= n[0]) ? null : next_tensor(item, n, dir, type, order, TI);
}
Expand Down Expand Up @@ -3876,7 +3951,7 @@ Tensor = Abacus.Tensor = Class(CombinatorialIterator, {

if ( "partial" === type )
{
index = Arithmetic.N($.data&&$.data.length ? $.data.indexOf(item) : -1);
index = Arithmetic.N(find($.data, item, true));
}
else
{
Expand Down
Loading

0 comments on commit 96271ff

Please sign in to comment.