Skip to content

Commit 0a572fa

Browse files
authored
Merge pull request #1022 from mathics/more-combinatorica
Expand via combinatorica 0.6 routines
2 parents eb6f2f4 + ce9e6b1 commit 0a572fa

4 files changed

Lines changed: 358 additions & 55 deletions

File tree

mathics/packages/DiscreteMath/CombinatoricaLite.m

Lines changed: 241 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@
2525
authors, Wolfram Research, or Cambridge University Press, their licensees,
2626
distributors and dealers shall in no event be liable for any indirect,
2727
incidental, or consequential damages.
28+
29+
And for the 0.6 version:
30+
Version 0.6 6/11/90 Beta Release
31+
Copyright (c) 1990 by Steven S. Skiena
32+
2833
*)
2934

3035
BeginPackage["DiscreteMath`CombinatoricaLite`"]
@@ -105,22 +110,42 @@
105110
UnrankPermutation[r_Integer, n_Integer?Positive] :=
106111
UnrankPermutation[r, Range[n]]
107112

113+
Compositions::usage = "Compositions[n, k] gives a list of all compositions of integer n into k parts."
114+
Compositions[n_Integer,k_Integer] :=
115+
Map[
116+
(Map[(#[[2]]-#[[1]]-1)&, Partition[Join[{0},#,{n+k}],2,1] ])&,
117+
KSubsets[Range[n+k-1],k-1]
118+
]
119+
108120
Cyclic::usage = "Cyclic is an argument to the Polya-theoretic functions ListNecklaces, NumberOfNecklace, and NecklacePolynomial, which count or enumerate distinct necklaces. Cyclic refers to the cyclic group acting on necklaces to make equivalent necklaces that can be obtained from each other by rotation.";
109121

110122
CyclicGroup::usage = "CyclicGroup[n] returns the cyclic group of permutations on n symbols.";
123+
CyclicGroup[0] := {{}}
124+
CyclicGroup[n_Integer] := Table[RotateRight[Range[n], i], {i, 0, n-1}]
111125

112126
DihedralGroupIndex::usage = "DihedralGroupIndex[n, x] returns the cycle index of the dihedral group on n symbols, expressed as a polynomial in x[1], x[2], ..., x[n].";
113127

114-
NecklacePolynomial::usage = "NecklacePolynomial[n, c, Cyclic] returns a polynomial in the colors in c whose coefficients represent numbers of ways of coloring an n-bead necklace with colors chosen from c, assuming that two colorings are equivalent if one can be obtained from the other by a rotation. NecklacePolynomial[n, c, Dihedral] is different in that it considers two colorings equivalent if one can be obtained from the other by a rotation or a flip or both.";
115-
116-
OrbitInventory::usage = "OrbitInventory[ci, x, w] returns the value of the cycle index ci when each formal variable x[i] is replaced by w. OrbitInventory[ci, x, weights] returns the inventory of orbits induced on a set of functions by the action of a group with cycle index ci. It is assumed that each element in the range of the functions is assigned a weight in list weights.";
128+
DihedralGroupIndex[n_Integer?Positive , x_Symbol] :=
129+
Expand[Simplify[CyclicGroupIndex[n, x]/2 +
130+
If[EvenQ[n],
131+
(x[2]^(n/2) + x[1]^2x[2]^(n/2-1))/4,
132+
(x[1]x[2]^((n-1)/2))/2
133+
]
134+
]
135+
]
117136

137+
(*
138+
NecklacePolynomial::usage = "NecklacePolynomial[n, c, Cyclic] returns a polynomial in the colors in c whose coefficients represent numbers of ways of coloring an n-bead necklace with colors chosen from c, assuming that two colorings are equivalent if one can be obtained from the other by a rotation. NecklacePolynomial[n, c, Dihedral] is different in that it considers two colorings equivalent if one can be obtained from the other by a rotation or a flip or both.";
118139
119140
NecklacePolynomial[n_Integer?Positive, c_, Cyclic] :=
120141
OrbitInventory[CyclicGroupIndex[n, x], x, c]
121142
122143
NecklacePolynomial[n_Integer?Positive, c_, Dihedral] :=
123144
OrbitInventory[DihedralGroupIndex[n, x], x, c]
145+
*)
146+
147+
OrbitInventory::usage = "OrbitInventory[ci, x, w] returns the value of the cycle index ci when each formal variable x[i] is replaced by w. OrbitInventory[ci, x, weights] returns the inventory of orbits induced on a set of functions by the action of a group with cycle index ci. It is assumed that each element in the range of the functions is assigned a weight in list weights.";
148+
124149

125150
OrbitInventory[ci_?PolynomialQ, x_Symbol, weights_List] :=
126151
Expand[ci /. Table[x[i] -> Apply[Plus, Map[#^i&, weights]],
@@ -131,17 +156,19 @@
131156
OrbitInventory[ci_?PolynomialQ, x_Symbol, r_] :=
132157
Expand[ci /. Table[x[i] -> r, {i, Exponent[ci, x[1]]} ]]
133158

134-
(*** Not working
135-
RandomPermutation::usage = "RandomPermutation[n] generates a random permutation of the first n natural numbers.";
136-
RP[n, _Integer] :=
137-
Module[{p = Range[n],i,x,t},
138-
Do [x = Random[Integer,{1,i}];
139-
t = p[[i]]; p[[i]] = p[[x]]; p[[x]] = t,
140-
{i,n,2,-1}
141-
];
142-
p
143-
]
144-
159+
(* Not working: always returns the same sorted value.
160+
We don't support Compile
161+
*)
162+
(***
163+
RP = Compile[{{n, _Integer}},
164+
Module[{p = Range[n],i,x,t},
165+
Do [x = Random[Integer,{1,i}];
166+
t = p[[i]]; p[[i]] = p[[x]]; p[[x]] = t,
167+
{i,n,2,-1}
168+
];
169+
p
170+
]
171+
]
145172
146173
RandomPermutation[n_Integer] := RP[n]
147174
RandomPermutation[l_List] := Permute[l, RP[Length[l]]]
@@ -164,6 +191,29 @@
164191
Subsets[n_Integer] := GrayCodeSubsets[Range[n]]
165192
*)
166193

194+
KSetPartitions::usage = "KSetPartitions[set, k] returns the list of set partitions of set with k blocks. KSetPartitions[n, k] returns the list of set partitions of {1, 2, ..., n} with k blocks. If all set partitions of a set are needed, use the function SetPartitions."
195+
KSetPartitions[{}, 0] := {{}}
196+
KSetPartitions[s_List, 0] := {}
197+
KSetPartitions[s_List, k_Integer] := {} /; (k > Length[s])
198+
KSetPartitions[s_List, k_Integer] := {Map[{#} &, s]} /; (k === Length[s])
199+
KSetPartitions[s_List, k_Integer] :=
200+
Block[{$RecursionLimit = Infinity},
201+
Join[Map[Prepend[#, {First[s]}] &, KSetPartitions[Rest[s], k - 1]],
202+
Flatten[
203+
Map[Table[Prepend[Delete[#, j], Prepend[#[[j]], s[[1]]]],
204+
{j, Length[#]}
205+
]&,
206+
KSetPartitions[Rest[s], k]
207+
], 1
208+
]
209+
]
210+
] /; (k > 0) && (k < Length[s])
211+
212+
KSetPartitions[0, 0] := {{}}
213+
KSetPartitions[0, k_Integer?Positive] := {}
214+
KSetPartitions[n_Integer?Positive, 0] := {}
215+
KSetPartitions[n_Integer?Positive, k_Integer?Positive] := KSetPartitions[Range[n], k]
216+
167217
(*
168218
KSubsets[l_List,0] := { {} }
169219
KSubsets[l_List,1] := Partition[l,1]
@@ -201,6 +251,20 @@
201251
Return[lo-1/2]
202252
];
203253
254+
*)
255+
256+
TransposePartition::usage = "TransposePartition[p] reflects a partition p of k parts along the main diagonal, creating a partition with maximum part k."
257+
TransposePartition[{}] := {}
258+
259+
TransposePartition[p_List] :=
260+
Module[{s=Select[p,(#>0)&], i, row, r},
261+
row = Length[s];
262+
Table [r = row; While [s[[row]]<=i, row--]; r, {i,First[s]}]
263+
]
264+
265+
266+
(*** FIXME: we run into recursion errors for nontrivial partitions. ***)
267+
(*
204268
Partitions::usage = "Partitions[n] constructs all partitions of integer n in reverse lexicographic order. Partitions[n, k] constructs all partitions of the integer n with maximum part at most k, in reverse lexicographic order."
205269
206270
Partitions[n_Integer] := Partitions[n,n]
@@ -218,4 +282,167 @@
218282
]
219283
*)
220284

285+
SetPartitions[{}] := {{}}
286+
SetPartitions[s_List] := Flatten[Table[KSetPartitions[s, i], {i, Length[s]}], 1]
287+
288+
SetPartitions[0] := {{}}
289+
SetPartitions[n_Integer?Positive] := SetPartitions[Range[n]]
290+
291+
LastLexicographicTableau::usage = "LastLexicographicTableau[p] constructs the last Young tableau with shape described by partition p."
292+
LastLexicographicTableau[s_List] :=
293+
Module[{c=0},
294+
Map[(c+=#; Range[c-#+1,c])&, s]
295+
]
296+
297+
(*
298+
NumberOfTableaux::usage = "NumberOfTableaux[p] uses the hook length formula to count the number of Young tableaux with shape defined by partition p."
299+
NumberOfTableaux[{}] := 1
300+
NumberOfTableaux[s_List] :=
301+
Module[{row,col,transpose=TransposePartition[s]},
302+
(Apply[Plus,s])! /
303+
Product [
304+
(transpose[[col]]-row+s[[row]]-col+1),
305+
{row,Length[s]}, {col,s[[row]]}
306+
]
307+
]
308+
309+
NumberOfTableaux[n_Integer] := Apply[Plus, Map[NumberOfTableaux, Partitions[n]]]
310+
*)
311+
312+
TransposePartition::usage = "TransposePartition[p] reflects a partition p of k parts along theg main diagonal, creating a partition with maximum part k."
313+
TransposePartition[{}] := {}
314+
315+
(*
316+
TransposePartition[p_List] :=
317+
Module[{s=Select[p,(#>0)&], i, row, r},
318+
row = Length[s];
319+
Table [r = row; While [s[[row]]<=i, row--]; r, {i,First[s]}]
320+
]
321+
*)
322+
323+
324+
Tableaux::usage = "Tableaux[p] constructs all tableaux having a shape given by integer partition p."
325+
Tableaux[s_List] :=
326+
Module[{t = LastLexicographicTableau[s]},
327+
Table[ t = NextTableau[t], {NumberOfTableaux[s]} ]
328+
]
329+
330+
Tableaux[n_Integer?Positive] := Apply[ Join, Map[ Tableaux, Partitions[n] ] ]
331+
332+
333+
(****************************************************************************
334+
*** Combinatorica 0.6 versions until we support more modern WL features *****
335+
*****************************************************************************)
336+
337+
(* Note: Until we support With[], this is the Combinatorica 0.6 version of BinarySearch *)
338+
BinarySearch::usage = "BinarySearch[l,k,f] searches sorted list l for key k and returns the the position of l containing k, with f a function which extracts the key from an element of l."
339+
BinarySearch[l_List,k_Integer] := BinarySearch[l,k,1,Length[l],Identity]
340+
BinarySearch[l_List,k_Integer,f_] := BinarySearch[l,k,1,Length[l],f]
341+
342+
BinarySearch[l_List,k_Integer,low_Integer,high_Integer,f_] :=
343+
Block[{mid = Floor[ (low + high)/2 ]},
344+
If [low > high, Return[low - 1/2]];
345+
If [f[ l[[mid]] ] == k, Return[mid]];
346+
If [f[ l[[mid]] ] > k,
347+
BinarySearch[l,k,1,mid-1,f],
348+
BinarySearch[l,k,mid+1,high,f]
349+
]
350+
]
351+
352+
KSubsets::usage = "KSubsets[l, k] gives all subsets of set l containing exactly k elements, ordered lexicographically."
353+
KSubsets[l_List,0] := { {} }
354+
KSubsets[l_List,1] := Partition[l,1]
355+
KSubsets[l_List,k_Integer?Positive] := {l} /; (k == Length[l])
356+
KSubsets[l_List,k_Integer?Positive] := {} /; (k > Length[l])
357+
358+
KSubsets[l_List,k_Integer?Positive] :=
359+
Join[
360+
Map[(Prepend[#,First[l]])&, KSubsets[Rest[l],k-1]],
361+
KSubsets[Rest[l],k]
362+
]
363+
364+
365+
(* Not working: always returns the same sorted value.
366+
Probably Sort[] below is buggy.
367+
*)
368+
(*
369+
RandomPermutation::usage = "RandomPermutation[n] returns a random permutation of length n."
370+
RandomPermutation1[n_Integer?Positive] :=
371+
Map[ Last, Sort[ Map[({Random[],#})&,Range[n]] ] ]
372+
373+
RandomPermutation2[n_Integer?Positive] :=
374+
Block[{p = Range[n],i,x},
375+
Do [
376+
x = Random[Integer,{1,i}];
377+
{p[[i]],p[[x]]} = {p[[x]],p[[i]]},
378+
{i,n,2,-1}
379+
];
380+
p
381+
]
382+
RandomPermutation[n_Integer?Positive] := RandomPermutation1[n]
383+
*)
384+
385+
(* Tableaux stuff not working. Hitting recursion limit....
386+
TransposeTableau::usage = "TransposeTableau[t] reflects a Young tableau t along the main diagonal, creating a different tableau."
387+
TransposeTableau[tb_List] :=
388+
Block[{t=Select[tb,(Length[#]>=1)&],row},
389+
Table[
390+
row = Map[First,t];
391+
t = Map[ Rest, Select[t,(Length[#]>1)&] ];
392+
row,
393+
{Length[First[tb]]}
394+
]
395+
]
396+
397+
TableauQ::usage = "TableauQ[t] returns True if and only if t represents a Young tableau."
398+
TableauQ[{}] = True
399+
TableauQ[t_List] :=
400+
And [
401+
Apply[ And, Map[(Apply[LessEqual,#])&,t] ],
402+
Apply[ And, Map[(Apply[LessEqual,#])&,TransposeTableau[t]] ],
403+
Apply[ GreaterEqual, Map[Length,t] ],
404+
Apply[ GreaterEqual, Map[Length,TransposeTableau[t]] ]
405+
]
406+
407+
408+
NextTableau::usage = "NextTableau[t] returns the tableau of shape t which follows t in lexicographic order."
409+
NextTableau[t_?TableauQ] :=
410+
Block[{s,y,row,j,count=0,tj,i,n=Max[t]},
411+
y = TableauToYVector[t];
412+
For [j=2, (j<n) && (y[[j]]>=y[[j-1]]), j++];
413+
If [y[[j]] >= y[[j-1]],
414+
Return[ FirstLexicographicTableau[ ShapeOfTableau[t] ] ]
415+
];
416+
s = ShapeOfTableau[ Table[Select[t[[i]],(#<=j)&], {i,Length[t]}] ];
417+
{row} = Last[ Position[ s, s[[ Position[t,j] [[1,1]] + 1 ]] ] ];
418+
s[[row]] --;
419+
tj = FirstLexicographicTableau[s];
420+
If[ Length[tj] < row,
421+
tj = Append[tj,{j}],
422+
tj[[row]] = Append[tj[[row]],j]
423+
];
424+
Join[
425+
Table[
426+
Join[tj[[i]],Select[t[[i]],(#>j)&]],
427+
{i,Length[tj]}
428+
],
429+
Table[t[[i]],{i,Length[tj]+1,Length[t]}]
430+
]
431+
]
432+
433+
434+
NumberOfTableaux::usage = "NumberOfTableaux[p] uses the hook length formula to count the number of Young tableaux with shape defined by partition p."
435+
NumberOfTableaux[{}] := 1
436+
NumberOfTableaux[s_List] :=
437+
Block[{row,col,transpose=TransposePartition[s]},
438+
(Apply[Plus,s])! /
439+
Product [
440+
(transpose[[col]]-row+s[[row]]-col+1),
441+
{row,Length[s]}, {col,s[[row]]}
442+
]
443+
]
444+
445+
NumberOfTableaux[n_Integer] := Apply[Plus, Map[NumberOfTableaux, Partitions[n]]]
446+
*)
447+
221448
EndPackage[]

test/helper.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from mathics.session import MathicsSession
2+
3+
session = MathicsSession(add_builtin=True, catch_interrupt=False)
4+
5+
def check_evaluation(str_expr: str, str_expected: str, message=""):
6+
"""Helper function to test that a WL expression against
7+
its results"""
8+
result = session.evaluate(str_expr)
9+
expected = session.evaluate(str_expected)
10+
11+
if message:
12+
assert result == expected, message
13+
else:
14+
assert result == expected

0 commit comments

Comments
 (0)