chromatic-matroids 0.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- chromatic_matroids-0.0.1/.gitignore +1 -0
- chromatic_matroids-0.0.1/LICENSE +19 -0
- chromatic_matroids-0.0.1/PKG-INFO +112 -0
- chromatic_matroids-0.0.1/README.md +97 -0
- chromatic_matroids-0.0.1/pyproject.toml +24 -0
- chromatic_matroids-0.0.1/src/chromatic_matroids/__init__.py +0 -0
- chromatic_matroids-0.0.1/src/chromatic_matroids/chromatic.py +55 -0
- chromatic_matroids-0.0.1/src/chromatic_matroids/chromatic_statistics_matorids.py +27 -0
- chromatic_matroids-0.0.1/src/chromatic_matroids/compositions.py +229 -0
- chromatic_matroids-0.0.1/src/chromatic_matroids/generate_matroids_utils.py +378 -0
- chromatic_matroids-0.0.1/src/chromatic_matroids/matrix_construction.py +229 -0
- chromatic_matroids-0.0.1/src/chromatic_matroids/matroids.py +159 -0
- chromatic_matroids-0.0.1/src/chromatic_matroids/ncquasisymmetric.py +66 -0
- chromatic_matroids-0.0.1/src/chromatic_matroids/quasisymmetric.py +67 -0
- chromatic_matroids-0.0.1/src/chromatic_matroids/setcompositions.py +336 -0
- chromatic_matroids-0.0.1/src/chromatic_matroids/stable_matroids_setcompositions.py +12 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
chromatic_matroids/dist/*
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Copyright (c) 2018 The Python Packaging Authority
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
in the Software without restriction, including without limitation the rights
|
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
furnished to do so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
SOFTWARE.
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: chromatic_matroids
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: A package that exposes some matroid operation and creations
|
|
5
|
+
Project-URL: Homepage, https://github.com/raulpenaguiao/chromatic-matroids/chromatic-matroids
|
|
6
|
+
Project-URL: Issues, https://github.com/raulpenaguiao/chromatic-matroids/chromatic-matroids/issues
|
|
7
|
+
Author: Sophie Rehberg
|
|
8
|
+
Author-email: Raul Penaguiao <raul.penaguiao@proton.me>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Requires-Python: >=3.8
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
16
|
+
# Matroid Package
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
This is a simple matroid package that contains constructor functions for basic combinatorial structures like set compositions, complext structures like matroids and algebraic structure like word quasisymmetric functions.
|
|
20
|
+
This builds up for the construction of chromatic invariants on matroids.
|
|
21
|
+
|
|
22
|
+
This abstracts away all the intricacies of matroid operations in order to compute the chromatic invariants.
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
## Packages
|
|
26
|
+
|
|
27
|
+
### Compositions
|
|
28
|
+
|
|
29
|
+
This package implements a construction of compositions:
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
print("Composition Construction")
|
|
33
|
+
comp1 = Composition([1, 2, 1])
|
|
34
|
+
comp2 = Composition([2, 1])
|
|
35
|
+
print(f"Example 5: Basic compositions: {comp1}, {comp2}")
|
|
36
|
+
print("\n")
|
|
37
|
+
|
|
38
|
+
print("Composition Operations")
|
|
39
|
+
print(f"Rest of {comp1}: {comp1.rest()}")
|
|
40
|
+
print(f"Prepending 3 to {comp2}: {comp2.prepend(3)}")
|
|
41
|
+
print("\n")
|
|
42
|
+
|
|
43
|
+
print("Generate All Compositions")
|
|
44
|
+
comps3 = Composition.generate_all_composition(3)
|
|
45
|
+
print(f"All compositions of 3: {comps3}\n")
|
|
46
|
+
print("\n")
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
### Set compositions
|
|
51
|
+
|
|
52
|
+
This package implements a construction of set compositions.
|
|
53
|
+
For a construction example, run the following:
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
print("Example 13: SetComposition Operations")
|
|
57
|
+
set_comp1 = SetComposition([[1, 2], [3, 4], [5]])
|
|
58
|
+
set_comp2 = SetComposition([[1], [2, 3]])
|
|
59
|
+
print(f"\nSet Compositions: {set_comp1}, {set_comp2}")
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
### Quasi-symmetric functions
|
|
64
|
+
|
|
65
|
+
This package implements a construction of quasi-symmetric functions.
|
|
66
|
+
For a construction example, run the following:
|
|
67
|
+
```
|
|
68
|
+
print("Construct QSymFunction from a dictionary of monomial bases")
|
|
69
|
+
qsym_from_dict = QSymFunction(monomial_basis={"(1,2,1)": 2, "(2,1)": -1})
|
|
70
|
+
print("QSymFunction from dictionary:")
|
|
71
|
+
print(f"coefficients: {qsym_from_dict.coefficients}")
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Word quasi-symmetric functions
|
|
75
|
+
|
|
76
|
+
This package implements a construction of word quasi-symmetric functions.
|
|
77
|
+
For a construction example, run the following:
|
|
78
|
+
```
|
|
79
|
+
print("Construct NCQSymFunctions from a dictionary of monomial bases")
|
|
80
|
+
nc_f = NCQSymFunction(monomial_basis=set_comp1)
|
|
81
|
+
nc_g = NCQSymFunction(monomial_basis=set_comp2)
|
|
82
|
+
nc_sum = nc_f + nc_g
|
|
83
|
+
print("Sum of NCQSymFunctions:")
|
|
84
|
+
print(f"nc_f: {nc_f.coefficients}")
|
|
85
|
+
print(f"nc_g: {nc_g.coefficients}")
|
|
86
|
+
print(f"nc_f + nc_g: {nc_sum.coefficients}")
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Graphs
|
|
90
|
+
This package implements a construction of graphs from edge list.
|
|
91
|
+
|
|
92
|
+
### Matroids
|
|
93
|
+
|
|
94
|
+
This package implements a construction of matroids from base list.
|
|
95
|
+
It also implements construction of *Schubert matroids*, *graphical matroids*, *uniform matroids*, and *nested matroids.
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
### Chromatic functions on matroids
|
|
99
|
+
|
|
100
|
+
This package implements a construction of chromatic invariants on matroids.
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
# To do
|
|
105
|
+
|
|
106
|
+
## Test cases
|
|
107
|
+
|
|
108
|
+
We are still building up our unit test library
|
|
109
|
+
|
|
110
|
+
## Efficiency
|
|
111
|
+
|
|
112
|
+
We are improving the construction of some methods related to computing stability of some set compositions with memorization.
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Matroid Package
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
This is a simple matroid package that contains constructor functions for basic combinatorial structures like set compositions, complext structures like matroids and algebraic structure like word quasisymmetric functions.
|
|
5
|
+
This builds up for the construction of chromatic invariants on matroids.
|
|
6
|
+
|
|
7
|
+
This abstracts away all the intricacies of matroid operations in order to compute the chromatic invariants.
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
## Packages
|
|
11
|
+
|
|
12
|
+
### Compositions
|
|
13
|
+
|
|
14
|
+
This package implements a construction of compositions:
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
print("Composition Construction")
|
|
18
|
+
comp1 = Composition([1, 2, 1])
|
|
19
|
+
comp2 = Composition([2, 1])
|
|
20
|
+
print(f"Example 5: Basic compositions: {comp1}, {comp2}")
|
|
21
|
+
print("\n")
|
|
22
|
+
|
|
23
|
+
print("Composition Operations")
|
|
24
|
+
print(f"Rest of {comp1}: {comp1.rest()}")
|
|
25
|
+
print(f"Prepending 3 to {comp2}: {comp2.prepend(3)}")
|
|
26
|
+
print("\n")
|
|
27
|
+
|
|
28
|
+
print("Generate All Compositions")
|
|
29
|
+
comps3 = Composition.generate_all_composition(3)
|
|
30
|
+
print(f"All compositions of 3: {comps3}\n")
|
|
31
|
+
print("\n")
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
### Set compositions
|
|
36
|
+
|
|
37
|
+
This package implements a construction of set compositions.
|
|
38
|
+
For a construction example, run the following:
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
print("Example 13: SetComposition Operations")
|
|
42
|
+
set_comp1 = SetComposition([[1, 2], [3, 4], [5]])
|
|
43
|
+
set_comp2 = SetComposition([[1], [2, 3]])
|
|
44
|
+
print(f"\nSet Compositions: {set_comp1}, {set_comp2}")
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
### Quasi-symmetric functions
|
|
49
|
+
|
|
50
|
+
This package implements a construction of quasi-symmetric functions.
|
|
51
|
+
For a construction example, run the following:
|
|
52
|
+
```
|
|
53
|
+
print("Construct QSymFunction from a dictionary of monomial bases")
|
|
54
|
+
qsym_from_dict = QSymFunction(monomial_basis={"(1,2,1)": 2, "(2,1)": -1})
|
|
55
|
+
print("QSymFunction from dictionary:")
|
|
56
|
+
print(f"coefficients: {qsym_from_dict.coefficients}")
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Word quasi-symmetric functions
|
|
60
|
+
|
|
61
|
+
This package implements a construction of word quasi-symmetric functions.
|
|
62
|
+
For a construction example, run the following:
|
|
63
|
+
```
|
|
64
|
+
print("Construct NCQSymFunctions from a dictionary of monomial bases")
|
|
65
|
+
nc_f = NCQSymFunction(monomial_basis=set_comp1)
|
|
66
|
+
nc_g = NCQSymFunction(monomial_basis=set_comp2)
|
|
67
|
+
nc_sum = nc_f + nc_g
|
|
68
|
+
print("Sum of NCQSymFunctions:")
|
|
69
|
+
print(f"nc_f: {nc_f.coefficients}")
|
|
70
|
+
print(f"nc_g: {nc_g.coefficients}")
|
|
71
|
+
print(f"nc_f + nc_g: {nc_sum.coefficients}")
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Graphs
|
|
75
|
+
This package implements a construction of graphs from edge list.
|
|
76
|
+
|
|
77
|
+
### Matroids
|
|
78
|
+
|
|
79
|
+
This package implements a construction of matroids from base list.
|
|
80
|
+
It also implements construction of *Schubert matroids*, *graphical matroids*, *uniform matroids*, and *nested matroids.
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
### Chromatic functions on matroids
|
|
84
|
+
|
|
85
|
+
This package implements a construction of chromatic invariants on matroids.
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
# To do
|
|
90
|
+
|
|
91
|
+
## Test cases
|
|
92
|
+
|
|
93
|
+
We are still building up our unit test library
|
|
94
|
+
|
|
95
|
+
## Efficiency
|
|
96
|
+
|
|
97
|
+
We are improving the construction of some methods related to computing stability of some set compositions with memorization.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "chromatic_matroids"
|
|
7
|
+
version = "0.0.1"
|
|
8
|
+
authors = [
|
|
9
|
+
{ name="Raul Penaguiao", email="raul.penaguiao@proton.me" },
|
|
10
|
+
{ name="Sophie Rehberg", email="" },
|
|
11
|
+
]
|
|
12
|
+
description = "A package that exposes some matroid operation and creations"
|
|
13
|
+
readme = "README.md"
|
|
14
|
+
requires-python = ">=3.8"
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Programming Language :: Python :: 3",
|
|
17
|
+
"Operating System :: OS Independent",
|
|
18
|
+
]
|
|
19
|
+
license = "MIT"
|
|
20
|
+
license-files = ["LICENSE"]
|
|
21
|
+
|
|
22
|
+
[project.urls]
|
|
23
|
+
Homepage = "https://github.com/raulpenaguiao/chromatic-matroids/chromatic-matroids"
|
|
24
|
+
Issues = "https://github.com/raulpenaguiao/chromatic-matroids/chromatic-matroids/issues"
|
|
File without changes
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# matroid_chromatic/chromatic.py
|
|
2
|
+
from typing import Dict
|
|
3
|
+
from matroid_chromatic import Matroid
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def compute_chromatic_noncommutative_qsym_function(matroid: Matroid) -> dict:
|
|
8
|
+
"""
|
|
9
|
+
Compute the non commutative qsym function as a dictionary that matches a
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def compute_chromatic_polynomial(matroid: Matroid) -> list:
|
|
15
|
+
"""
|
|
16
|
+
Compute the chromatic polynomial as a list of coefficients.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
matroid: The matroid
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
Value of chromatic polynomial at q
|
|
23
|
+
"""
|
|
24
|
+
def mobius_function(X: frozenset, Y: frozenset) -> int:
|
|
25
|
+
"""Compute the Möbius function value."""
|
|
26
|
+
if X == Y:
|
|
27
|
+
return 1
|
|
28
|
+
if not X.issubset(Y):
|
|
29
|
+
return 0
|
|
30
|
+
|
|
31
|
+
result = 0
|
|
32
|
+
for Z in range_sets[len(X):len(Y)]:
|
|
33
|
+
if X.issubset(Z) and Z.issubset(Y):
|
|
34
|
+
result -= mobius_function(X, Z)
|
|
35
|
+
return result
|
|
36
|
+
|
|
37
|
+
# Generate all sets between empty set and ground set
|
|
38
|
+
range_sets = []
|
|
39
|
+
for i in range(len(matroid.ground_set) + 1):
|
|
40
|
+
current_level = set()
|
|
41
|
+
for ind_set in matroid.independent_sets():
|
|
42
|
+
if len(ind_set) == i:
|
|
43
|
+
current_level.add(ind_set)
|
|
44
|
+
range_sets.append(current_level)
|
|
45
|
+
|
|
46
|
+
# Compute chromatic polynomial using Möbius inversion
|
|
47
|
+
result = [0 for _ in range(len(matroid.ground_set) + 1)]
|
|
48
|
+
empty_set = frozenset()
|
|
49
|
+
ground_set = frozenset(matroid.ground_set)
|
|
50
|
+
|
|
51
|
+
for X in matroid.independent_sets():
|
|
52
|
+
coeff = mobius_function(empty_set, X)
|
|
53
|
+
result[len(X)] += coeff
|
|
54
|
+
|
|
55
|
+
return result
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# matroid_chromatic/chromatic_statistics_matorids.py
|
|
2
|
+
from matroid_chromatic import Matroid
|
|
3
|
+
from matroid_chromatic import SetComposition
|
|
4
|
+
from matroid_chromatic import Composition
|
|
5
|
+
from matroid_chromatic import QSymFunction
|
|
6
|
+
from matroid_chromatic import NCQSymFunction
|
|
7
|
+
from matroid_chromatic import stable_matroids_setcompositions
|
|
8
|
+
|
|
9
|
+
def chromatic_quasisymmetric_function(matroid: Matroid) -> QSymFunction:
|
|
10
|
+
"""
|
|
11
|
+
Compute the chromatic quasisymmetric function of a matroid
|
|
12
|
+
"""
|
|
13
|
+
return NCQSymFunction.comu(chromatic_non_commutative_quasisymmetric_function(matroid))
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def chromatic_non_commutative_quasisymmetric_function(matroid: Matroid) -> NCQSymFunction:
|
|
17
|
+
"""
|
|
18
|
+
Compute the chromatic non-commutative quasisymmetric function of a matroid
|
|
19
|
+
"""
|
|
20
|
+
ground_list = list(matroid.ground_set)
|
|
21
|
+
relabeling_map = {i+1: ground_list[i] for i in range(len(ground_list))}
|
|
22
|
+
all_set_compositions = [opi.relabel(relabeling_map) for opi in SetComposition.generate_all_setcompositions(len(ground_list))]
|
|
23
|
+
coeffs = {str(opi): 1 for opi in all_set_compositions
|
|
24
|
+
if stable_matroids_setcompositions(matroid, opi)}
|
|
25
|
+
return NCQSymFunction(monomial_basis=coeffs)
|
|
26
|
+
|
|
27
|
+
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
# matroid_chromatic/compositions.py
|
|
2
|
+
from typing import Set, List, Tuple
|
|
3
|
+
|
|
4
|
+
class Composition:
|
|
5
|
+
"""
|
|
6
|
+
A class representing compositions of integers as an imutable class. This means no methods change self.
|
|
7
|
+
A composition of n is a way of writing n as a sum of positive integers.
|
|
8
|
+
For example, [2,1,3] is a composition of 6.
|
|
9
|
+
|
|
10
|
+
This class implements quasi-shuffles of compositions
|
|
11
|
+
"""
|
|
12
|
+
# Class variables for memoization of quasi-shuffle computations
|
|
13
|
+
_PREGEN = 4
|
|
14
|
+
_all_generated_compositions_size = 0 # Limit for precomputation
|
|
15
|
+
_all_generated_compositions_list = [] # Cache for precomputed results
|
|
16
|
+
_qshuffle_precomputation_dictionary = {} # Cache for precomputed results
|
|
17
|
+
|
|
18
|
+
def __init__(self, *args, **kwargs):
|
|
19
|
+
"""
|
|
20
|
+
Initialize a composition of integers
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
*args: Variable length argument list. Can be a list or a tuple of positive integers
|
|
24
|
+
**kwargs: Arbitrary keyword arguments (reserved for future use)
|
|
25
|
+
|
|
26
|
+
Raises:
|
|
27
|
+
TypeError: If any argument is not an integer
|
|
28
|
+
ValueError: If any integer is not positive
|
|
29
|
+
"""
|
|
30
|
+
# Check if args is a single list/tuple
|
|
31
|
+
if len(args) == 0:#Composition() = [] the unique composition of 0
|
|
32
|
+
self.parts = []
|
|
33
|
+
elif len(args) == 1:
|
|
34
|
+
if isinstance(args[0], (list, tuple)):
|
|
35
|
+
self.parts = list(args[0]) # Convert to list to ensure mutability
|
|
36
|
+
elif isinstance(args[0], str):
|
|
37
|
+
if args[0] == "()":
|
|
38
|
+
self.parts = []#Composition('()') = [] the unique composition of 0
|
|
39
|
+
else:
|
|
40
|
+
try:
|
|
41
|
+
self.parts = [int(i) for i in args[0][1:-1].split(",")]#Composition('(2,1,3)') = [2,1,3]
|
|
42
|
+
except:
|
|
43
|
+
raise Exception(f"Composition input malformed, expected a string of the form '(2,1,3)', got {args[0]}")
|
|
44
|
+
else:
|
|
45
|
+
raise Exception("")
|
|
46
|
+
else:
|
|
47
|
+
raise Exception("")
|
|
48
|
+
# Validate all arguments are positive integers
|
|
49
|
+
for part in self.parts:
|
|
50
|
+
if not isinstance(part, int):
|
|
51
|
+
raise TypeError(f"All parts must be integers, got {type(part)}")
|
|
52
|
+
if part <= 0:
|
|
53
|
+
raise ValueError(f"All parts must be positive integers, got {part}")
|
|
54
|
+
|
|
55
|
+
self.nparts = len(self.parts) # Number of parts in the composition
|
|
56
|
+
self.n = sum(self.parts) # Total sum of all parts
|
|
57
|
+
|
|
58
|
+
def __str__(self) -> str:
|
|
59
|
+
"""
|
|
60
|
+
String representation of the composition using | as separator
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
str: Composition parts joined by '|' character
|
|
64
|
+
"""
|
|
65
|
+
return "(" + ",".join([str(i) for i in self.parts]) + ")"
|
|
66
|
+
|
|
67
|
+
def __repr__(self) -> str:
|
|
68
|
+
return self.__str__()
|
|
69
|
+
|
|
70
|
+
def __eq__(self, other) -> bool:
|
|
71
|
+
if not self.n == other.n:
|
|
72
|
+
return False
|
|
73
|
+
if not self.nparts == other.nparts:
|
|
74
|
+
return False
|
|
75
|
+
for a, b in zip(self.parts, other.parts):
|
|
76
|
+
if not a == b:
|
|
77
|
+
return False
|
|
78
|
+
return True
|
|
79
|
+
|
|
80
|
+
def rest(self) -> 'Composition':
|
|
81
|
+
"""
|
|
82
|
+
Returns a new Composition object containing all but the first part
|
|
83
|
+
of this composition.
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
Composition: A new composition without the first part
|
|
87
|
+
"""
|
|
88
|
+
if len(self.parts) == 0:
|
|
89
|
+
raise Exception("Empty composition cannot be rest-ed")
|
|
90
|
+
return Composition(self.parts[1:])
|
|
91
|
+
|
|
92
|
+
def prepend(self, a: int) -> 'Composition':
|
|
93
|
+
"""
|
|
94
|
+
Computes a new composition with a prepend element
|
|
95
|
+
Does not change self
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
self (Composition): a composition
|
|
99
|
+
a (int): term to prepend
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
Composition: A new composition with a prepend element
|
|
103
|
+
"""
|
|
104
|
+
return Composition([a] + self.parts)
|
|
105
|
+
|
|
106
|
+
@staticmethod
|
|
107
|
+
def generate_all_composition(n: int) -> list['Composition']:
|
|
108
|
+
"""
|
|
109
|
+
Computes all compositions of a given size iteratively with memoization
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
n (int): The positive integer to generate compositions for
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
list: A list of all compositions of n, ordered lexicographically
|
|
116
|
+
For example, generate_all_composition(3) returns:
|
|
117
|
+
[[3], [2,1], [1,2], [1,1,1]]
|
|
118
|
+
|
|
119
|
+
Note:
|
|
120
|
+
Uses memoization to avoid recomputing previously generated compositions.
|
|
121
|
+
Results are stored in _all_generated_compositions_list for future use.
|
|
122
|
+
"""
|
|
123
|
+
# Check if these compositions were already generated
|
|
124
|
+
if n < Composition._all_generated_compositions_size:
|
|
125
|
+
if n < 0:
|
|
126
|
+
return [] # No compositions for negative numbers
|
|
127
|
+
return Composition._all_generated_compositions_list[n]
|
|
128
|
+
|
|
129
|
+
# Start with the trivial composition [n]
|
|
130
|
+
answer = [Composition([n])]
|
|
131
|
+
|
|
132
|
+
# Generate all compositions by breaking off initial parts
|
|
133
|
+
for k in range(1, n):
|
|
134
|
+
# For each k from 1 to n-1:
|
|
135
|
+
# Take each composition of k and prepend (n-k) to it
|
|
136
|
+
# This systematically generates all possible compositions
|
|
137
|
+
smaller_compositions = Composition.generate_all_composition(k)
|
|
138
|
+
for alpha in smaller_compositions:
|
|
139
|
+
answer.append(alpha.prepend(n-k))
|
|
140
|
+
|
|
141
|
+
# Update the memoization cache
|
|
142
|
+
Composition._all_generated_compositions_size = n + 1
|
|
143
|
+
Composition._all_generated_compositions_list += [answer]
|
|
144
|
+
return answer
|
|
145
|
+
|
|
146
|
+
@staticmethod
|
|
147
|
+
def quasi_shuffles(q: 'Composition', t: 'Composition') -> dict[str:int]:
|
|
148
|
+
"""
|
|
149
|
+
Compute all possible quasi-shuffles of two compositions.
|
|
150
|
+
A quasi-shuffle combines two compositions allowing both interleaving and addition
|
|
151
|
+
of corresponding terms.
|
|
152
|
+
|
|
153
|
+
For example, quasi-shuffling [1,2] and [3,1] could give:
|
|
154
|
+
- [1,3,2,1] (interleaving)
|
|
155
|
+
- [4,2,1] (adding first terms)
|
|
156
|
+
- [1,5,1] (adding first and second terms)
|
|
157
|
+
And other combinations...
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
q (Composition): First composition
|
|
161
|
+
t (Composition): Second composition
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
dict: Dictionary mapping resulting compositions to their coefficients.
|
|
165
|
+
The coefficients track how many ways that composition can be formed.
|
|
166
|
+
|
|
167
|
+
Note:
|
|
168
|
+
Uses the recursive formula:
|
|
169
|
+
qs(a·q, b·t) = a·qs(q, bt) + b·qs(aq, t) + (a+b)·qs(q, t)
|
|
170
|
+
where a·q means composition q with 'a' prepended
|
|
171
|
+
"""
|
|
172
|
+
# Check memoization cache
|
|
173
|
+
if str(q) in Composition._qshuffle_precomputation_dictionary:
|
|
174
|
+
if str(t) in Composition._qshuffle_precomputation_dictionary[str(q)]:
|
|
175
|
+
return Composition._qshuffle_precomputation_dictionary[str(q)][str(t)]
|
|
176
|
+
|
|
177
|
+
# Base cases: if either composition is empty, return the other
|
|
178
|
+
if q.n == 0:
|
|
179
|
+
return {str(t): 1}
|
|
180
|
+
if t.n == 0:
|
|
181
|
+
return {str(q): 1}
|
|
182
|
+
|
|
183
|
+
# Get the first terms and rest of each composition
|
|
184
|
+
a = q.parts[0]
|
|
185
|
+
b = t.parts[0]
|
|
186
|
+
qrest = q.rest()
|
|
187
|
+
trest = t.rest()
|
|
188
|
+
|
|
189
|
+
answer = {}
|
|
190
|
+
|
|
191
|
+
# Case 1: Take 'a' from first composition > a·qs(q, bt)
|
|
192
|
+
qsh1 = Composition.quasi_shuffles(qrest, t)
|
|
193
|
+
for alphas in qsh1:
|
|
194
|
+
newqs = str(Composition(alphas).prepend(a))
|
|
195
|
+
if not(newqs in answer):
|
|
196
|
+
answer[newqs] = 0
|
|
197
|
+
answer[newqs] += qsh1[alphas]
|
|
198
|
+
|
|
199
|
+
# Case 2: Take 'b' from second composition > + b·qs(aq, t)
|
|
200
|
+
qsh2 = Composition.quasi_shuffles(q, trest)
|
|
201
|
+
for alphas in qsh2:
|
|
202
|
+
newqs = str(Composition(alphas).prepend(b))
|
|
203
|
+
if not(newqs in answer):
|
|
204
|
+
answer[newqs] = 0
|
|
205
|
+
answer[newqs] += qsh2[alphas]
|
|
206
|
+
|
|
207
|
+
# Case 3: Take both first terms and add them together > (a+b)·qs(q, t)
|
|
208
|
+
qsh3 = Composition.quasi_shuffles(qrest, trest)
|
|
209
|
+
for alphas in qsh3:
|
|
210
|
+
newqs = str(Composition(alphas).prepend(a + b))
|
|
211
|
+
if not(newqs in answer):
|
|
212
|
+
answer[newqs] = 0
|
|
213
|
+
answer[newqs] += qsh3[alphas]
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
if not str(q) in Composition._qshuffle_precomputation_dictionary:
|
|
217
|
+
Composition._qshuffle_precomputation_dictionary[str(q)] = {}
|
|
218
|
+
Composition._qshuffle_precomputation_dictionary[str(q)][str(t)] = answer
|
|
219
|
+
return answer
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
Composition._all_generated_compositions_list = [[Composition()]]
|
|
223
|
+
# Pregenerate all compositions and qshuffles up to size PREGEN
|
|
224
|
+
Composition.generate_all_composition(Composition._PREGEN)
|
|
225
|
+
for comp_list1 in Composition._all_generated_compositions_list:
|
|
226
|
+
for comp1 in comp_list1:
|
|
227
|
+
for comp_list2 in Composition._all_generated_compositions_list:
|
|
228
|
+
for comp2 in comp_list2:
|
|
229
|
+
Composition.quasi_shuffles(comp1, comp2)
|