quill-sort 3.0.0__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.
- quill_sort-3.0.0/PKG-INFO +139 -0
- quill_sort-3.0.0/README.md +107 -0
- quill_sort-3.0.0/pyproject.toml +53 -0
- quill_sort-3.0.0/quill/__init__.py +146 -0
- quill_sort-3.0.0/quill/__main__.py +117 -0
- quill_sort-3.0.0/quill/_core.py +189 -0
- quill_sort-3.0.0/quill/_parallel.py +191 -0
- quill_sort-3.0.0/quill/_plugins.py +269 -0
- quill_sort-3.0.0/quill/_profile.py +97 -0
- quill_sort-3.0.0/quill/_strategies.py +156 -0
- quill_sort-3.0.0/quill_sort.egg-info/PKG-INFO +139 -0
- quill_sort-3.0.0/quill_sort.egg-info/SOURCES.txt +15 -0
- quill_sort-3.0.0/quill_sort.egg-info/dependency_links.txt +1 -0
- quill_sort-3.0.0/quill_sort.egg-info/entry_points.txt +2 -0
- quill_sort-3.0.0/quill_sort.egg-info/requires.txt +11 -0
- quill_sort-3.0.0/quill_sort.egg-info/top_level.txt +3 -0
- quill_sort-3.0.0/setup.cfg +4 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: quill-sort
|
|
3
|
+
Version: 3.0.0
|
|
4
|
+
Summary: Adaptive ultra-sort: the fastest general-purpose sorting library for Python
|
|
5
|
+
Author: Invariant Games
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/invariant-games/quill
|
|
8
|
+
Project-URL: Repository, https://github.com/invariant-games/quill
|
|
9
|
+
Keywords: sorting,sort,algorithm,radix sort,fast sort,high performance,numpy
|
|
10
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering
|
|
22
|
+
Requires-Python: >=3.8
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
Provides-Extra: fast
|
|
25
|
+
Requires-Dist: numpy>=1.21; extra == "fast"
|
|
26
|
+
Provides-Extra: pandas
|
|
27
|
+
Requires-Dist: numpy>=1.21; extra == "pandas"
|
|
28
|
+
Requires-Dist: pandas>=1.3; extra == "pandas"
|
|
29
|
+
Provides-Extra: all
|
|
30
|
+
Requires-Dist: numpy>=1.21; extra == "all"
|
|
31
|
+
Requires-Dist: pandas>=1.3; extra == "all"
|
|
32
|
+
|
|
33
|
+
# quill-sort
|
|
34
|
+
|
|
35
|
+
**Adaptive Ultra-Sort** — the fastest general-purpose sorting library for Python.
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
from quill import quill_sort, quill_sorted
|
|
39
|
+
|
|
40
|
+
quill_sort([3, 1, 4, 1, 5, 9]) # → [1, 1, 3, 4, 5, 9]
|
|
41
|
+
quill_sort(records, key=lambda r: r['age']) # sort objects
|
|
42
|
+
quill_sort(big_data, parallel=True) # use all CPU cores
|
|
43
|
+
result = quill_sorted(iterable, reverse=True) # mirrors built-in sorted()
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Installation
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
pip install quill-sort # core (no dependencies)
|
|
50
|
+
pip install quill-sort[fast] # + numpy for max speed
|
|
51
|
+
pip install quill-sort[all] # + numpy + pandas support
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## How it works
|
|
55
|
+
|
|
56
|
+
Quill profiles your data at intake and picks the optimal path automatically:
|
|
57
|
+
|
|
58
|
+
| Data type | Strategy | Complexity |
|
|
59
|
+
|-----------|----------|------------|
|
|
60
|
+
| Dense integer range | Counting sort | O(n + k) |
|
|
61
|
+
| Integers (numpy) | Narrowest-dtype radix | O(n) |
|
|
62
|
+
| Integers (no numpy) | Base-256 radix, precomputed histograms | O(n) |
|
|
63
|
+
| Floats | NumPy optimised introsort | O(n log n) |
|
|
64
|
+
| Strings / bytes | Python Timsort | O(n log n) |
|
|
65
|
+
| Objects with key | Rank-encode → numpy argsort | O(n log n) |
|
|
66
|
+
| Pre-sorted | Early exit after O(sample) probe | O(sample) |
|
|
67
|
+
| Reverse-sorted | Single `.reverse()` call | O(n/2) |
|
|
68
|
+
|
|
69
|
+
**Dtype-width optimisation** — Quill selects the narrowest safe numpy integer
|
|
70
|
+
type for each dataset. `uint16` uses 4× less memory bandwidth than `int64`,
|
|
71
|
+
making it ~6× faster on large arrays. This is the same technique used by
|
|
72
|
+
`ska_sort` and `spread_sort`.
|
|
73
|
+
|
|
74
|
+
**Single-pass histogram precomputation** — the pure-Python fallback scans the
|
|
75
|
+
array exactly once to build all digit histograms simultaneously, then skips any
|
|
76
|
+
radix pass where all values share the same digit.
|
|
77
|
+
|
|
78
|
+
## Supported types
|
|
79
|
+
|
|
80
|
+
- `int`, `float`, `str`, `bytes` — native fast paths
|
|
81
|
+
- Negative integers — automatically shifted to non-negative before sorting
|
|
82
|
+
- `None` values — filtered out, sorted data returned, Nones reinserted at end
|
|
83
|
+
- `pandas.Series` — sorted and returned as a new Series
|
|
84
|
+
- `pandas.DataFrame` — sorted by column(s) via `key='column_name'`
|
|
85
|
+
- `numpy.ndarray` — sorted in-place via numpy directly
|
|
86
|
+
- Any generator or iterator — materialised to list, sorted, returned
|
|
87
|
+
|
|
88
|
+
## Plugin system
|
|
89
|
+
|
|
90
|
+
Add support for any custom type:
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
from quill import register_plugin
|
|
94
|
+
from quill._plugins import QuillPlugin
|
|
95
|
+
|
|
96
|
+
class MyPlugin(QuillPlugin):
|
|
97
|
+
handles = (MyCustomClass,)
|
|
98
|
+
name = "my_custom_class"
|
|
99
|
+
|
|
100
|
+
@staticmethod
|
|
101
|
+
def prepare(data, key, reverse):
|
|
102
|
+
items = [x.value for x in data]
|
|
103
|
+
postprocess = lambda sorted_list: [MyCustomClass(v) for v in sorted_list]
|
|
104
|
+
return items, key, postprocess
|
|
105
|
+
|
|
106
|
+
register_plugin(MyPlugin)
|
|
107
|
+
|
|
108
|
+
# Now quill_sort works on your type automatically:
|
|
109
|
+
quill_sort(list_of_my_objects)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## CLI / Demo
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
python -m quill # run the benchmark demo
|
|
116
|
+
quill # same, if installed via pip
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Performance
|
|
120
|
+
|
|
121
|
+
Approximate timings on modern hardware (Apple M2, numpy installed):
|
|
122
|
+
|
|
123
|
+
| n | Time |
|
|
124
|
+
|---|------|
|
|
125
|
+
| 1,000 | 0.00008s |
|
|
126
|
+
| 100,000 | 0.002s |
|
|
127
|
+
| 1,000,000 | 0.015s |
|
|
128
|
+
| 10,000,000 | 0.15s |
|
|
129
|
+
| 100,000,000 | 1.5s |
|
|
130
|
+
|
|
131
|
+
## Requirements
|
|
132
|
+
|
|
133
|
+
- Python 3.8+
|
|
134
|
+
- `numpy` optional (strongly recommended — `pip install numpy`)
|
|
135
|
+
- `pandas` optional (for DataFrame support)
|
|
136
|
+
|
|
137
|
+
## License
|
|
138
|
+
|
|
139
|
+
MIT — by Invariant Games
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# quill-sort
|
|
2
|
+
|
|
3
|
+
**Adaptive Ultra-Sort** — the fastest general-purpose sorting library for Python.
|
|
4
|
+
|
|
5
|
+
```python
|
|
6
|
+
from quill import quill_sort, quill_sorted
|
|
7
|
+
|
|
8
|
+
quill_sort([3, 1, 4, 1, 5, 9]) # → [1, 1, 3, 4, 5, 9]
|
|
9
|
+
quill_sort(records, key=lambda r: r['age']) # sort objects
|
|
10
|
+
quill_sort(big_data, parallel=True) # use all CPU cores
|
|
11
|
+
result = quill_sorted(iterable, reverse=True) # mirrors built-in sorted()
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pip install quill-sort # core (no dependencies)
|
|
18
|
+
pip install quill-sort[fast] # + numpy for max speed
|
|
19
|
+
pip install quill-sort[all] # + numpy + pandas support
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## How it works
|
|
23
|
+
|
|
24
|
+
Quill profiles your data at intake and picks the optimal path automatically:
|
|
25
|
+
|
|
26
|
+
| Data type | Strategy | Complexity |
|
|
27
|
+
|-----------|----------|------------|
|
|
28
|
+
| Dense integer range | Counting sort | O(n + k) |
|
|
29
|
+
| Integers (numpy) | Narrowest-dtype radix | O(n) |
|
|
30
|
+
| Integers (no numpy) | Base-256 radix, precomputed histograms | O(n) |
|
|
31
|
+
| Floats | NumPy optimised introsort | O(n log n) |
|
|
32
|
+
| Strings / bytes | Python Timsort | O(n log n) |
|
|
33
|
+
| Objects with key | Rank-encode → numpy argsort | O(n log n) |
|
|
34
|
+
| Pre-sorted | Early exit after O(sample) probe | O(sample) |
|
|
35
|
+
| Reverse-sorted | Single `.reverse()` call | O(n/2) |
|
|
36
|
+
|
|
37
|
+
**Dtype-width optimisation** — Quill selects the narrowest safe numpy integer
|
|
38
|
+
type for each dataset. `uint16` uses 4× less memory bandwidth than `int64`,
|
|
39
|
+
making it ~6× faster on large arrays. This is the same technique used by
|
|
40
|
+
`ska_sort` and `spread_sort`.
|
|
41
|
+
|
|
42
|
+
**Single-pass histogram precomputation** — the pure-Python fallback scans the
|
|
43
|
+
array exactly once to build all digit histograms simultaneously, then skips any
|
|
44
|
+
radix pass where all values share the same digit.
|
|
45
|
+
|
|
46
|
+
## Supported types
|
|
47
|
+
|
|
48
|
+
- `int`, `float`, `str`, `bytes` — native fast paths
|
|
49
|
+
- Negative integers — automatically shifted to non-negative before sorting
|
|
50
|
+
- `None` values — filtered out, sorted data returned, Nones reinserted at end
|
|
51
|
+
- `pandas.Series` — sorted and returned as a new Series
|
|
52
|
+
- `pandas.DataFrame` — sorted by column(s) via `key='column_name'`
|
|
53
|
+
- `numpy.ndarray` — sorted in-place via numpy directly
|
|
54
|
+
- Any generator or iterator — materialised to list, sorted, returned
|
|
55
|
+
|
|
56
|
+
## Plugin system
|
|
57
|
+
|
|
58
|
+
Add support for any custom type:
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
from quill import register_plugin
|
|
62
|
+
from quill._plugins import QuillPlugin
|
|
63
|
+
|
|
64
|
+
class MyPlugin(QuillPlugin):
|
|
65
|
+
handles = (MyCustomClass,)
|
|
66
|
+
name = "my_custom_class"
|
|
67
|
+
|
|
68
|
+
@staticmethod
|
|
69
|
+
def prepare(data, key, reverse):
|
|
70
|
+
items = [x.value for x in data]
|
|
71
|
+
postprocess = lambda sorted_list: [MyCustomClass(v) for v in sorted_list]
|
|
72
|
+
return items, key, postprocess
|
|
73
|
+
|
|
74
|
+
register_plugin(MyPlugin)
|
|
75
|
+
|
|
76
|
+
# Now quill_sort works on your type automatically:
|
|
77
|
+
quill_sort(list_of_my_objects)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## CLI / Demo
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
python -m quill # run the benchmark demo
|
|
84
|
+
quill # same, if installed via pip
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Performance
|
|
88
|
+
|
|
89
|
+
Approximate timings on modern hardware (Apple M2, numpy installed):
|
|
90
|
+
|
|
91
|
+
| n | Time |
|
|
92
|
+
|---|------|
|
|
93
|
+
| 1,000 | 0.00008s |
|
|
94
|
+
| 100,000 | 0.002s |
|
|
95
|
+
| 1,000,000 | 0.015s |
|
|
96
|
+
| 10,000,000 | 0.15s |
|
|
97
|
+
| 100,000,000 | 1.5s |
|
|
98
|
+
|
|
99
|
+
## Requirements
|
|
100
|
+
|
|
101
|
+
- Python 3.8+
|
|
102
|
+
- `numpy` optional (strongly recommended — `pip install numpy`)
|
|
103
|
+
- `pandas` optional (for DataFrame support)
|
|
104
|
+
|
|
105
|
+
## License
|
|
106
|
+
|
|
107
|
+
MIT — by Invariant Games
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "quill-sort"
|
|
7
|
+
version = "3.0.0"
|
|
8
|
+
description = "Adaptive ultra-sort: the fastest general-purpose sorting library for Python"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "MIT" }
|
|
11
|
+
authors = [{ name = "Invariant Games" }]
|
|
12
|
+
requires-python = ">=3.8"
|
|
13
|
+
|
|
14
|
+
keywords = [
|
|
15
|
+
"sorting", "sort", "algorithm", "radix sort",
|
|
16
|
+
"fast sort", "high performance", "numpy"
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
classifiers = [
|
|
20
|
+
"Development Status :: 5 - Production/Stable",
|
|
21
|
+
"Intended Audience :: Developers",
|
|
22
|
+
"Intended Audience :: Science/Research",
|
|
23
|
+
"License :: OSI Approved :: MIT License",
|
|
24
|
+
"Programming Language :: Python :: 3",
|
|
25
|
+
"Programming Language :: Python :: 3.8",
|
|
26
|
+
"Programming Language :: Python :: 3.9",
|
|
27
|
+
"Programming Language :: Python :: 3.10",
|
|
28
|
+
"Programming Language :: Python :: 3.11",
|
|
29
|
+
"Programming Language :: Python :: 3.12",
|
|
30
|
+
"Topic :: Software Development :: Libraries",
|
|
31
|
+
"Topic :: Scientific/Engineering",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
# numpy is optional but strongly recommended
|
|
35
|
+
dependencies = []
|
|
36
|
+
|
|
37
|
+
[project.optional-dependencies]
|
|
38
|
+
fast = ["numpy>=1.21"]
|
|
39
|
+
pandas = ["numpy>=1.21", "pandas>=1.3"]
|
|
40
|
+
all = ["numpy>=1.21", "pandas>=1.3"]
|
|
41
|
+
|
|
42
|
+
[project.scripts]
|
|
43
|
+
quill = "quill.__main__:main"
|
|
44
|
+
|
|
45
|
+
[project.urls]
|
|
46
|
+
Homepage = "https://github.com/invariant-games/quill"
|
|
47
|
+
Repository = "https://github.com/invariant-games/quill"
|
|
48
|
+
|
|
49
|
+
[tool.setuptools.packages.find]
|
|
50
|
+
where = ["."]
|
|
51
|
+
|
|
52
|
+
[tool.setuptools.package-data]
|
|
53
|
+
quill = ["py.typed"]
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"""
|
|
2
|
+
quill — Adaptive Ultra-Sort
|
|
3
|
+
============================
|
|
4
|
+
High-performance, general-purpose sorting for Python.
|
|
5
|
+
|
|
6
|
+
from quill import quill_sort, quill_sorted, register_plugin
|
|
7
|
+
|
|
8
|
+
QUICK REFERENCE
|
|
9
|
+
---------------
|
|
10
|
+
quill_sort(data) # in-place, integers/floats/strings
|
|
11
|
+
quill_sort(data, key=lambda x: x['score']) # sort objects by key
|
|
12
|
+
quill_sort(data, reverse=True) # descending
|
|
13
|
+
quill_sort(data, inplace=False) # returns new list
|
|
14
|
+
quill_sort(data, parallel=True) # use all CPU cores
|
|
15
|
+
|
|
16
|
+
result = quill_sorted(iterable) # mirrors built-in sorted()
|
|
17
|
+
result = quill_sorted(iterable, key=str.lower, reverse=True)
|
|
18
|
+
|
|
19
|
+
register_plugin(MyPlugin) # add support for custom types
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
from typing import Callable, Iterable, Optional
|
|
24
|
+
|
|
25
|
+
from ._core import quill_sort_impl
|
|
26
|
+
from ._plugins import QuillPlugin, register_plugin, probe_plugins
|
|
27
|
+
|
|
28
|
+
__version__ = "3.0.0"
|
|
29
|
+
__author__ = "Invariant Games"
|
|
30
|
+
__all__ = ["quill_sort", "quill_sorted", "QuillPlugin", "register_plugin"]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def quill_sort(
|
|
34
|
+
data : list,
|
|
35
|
+
key : Optional[Callable] = None,
|
|
36
|
+
reverse : bool = False,
|
|
37
|
+
inplace : bool = True,
|
|
38
|
+
parallel : bool = False,
|
|
39
|
+
) -> list:
|
|
40
|
+
"""
|
|
41
|
+
Sort `data` using Quill's adaptive ultra-sort engine.
|
|
42
|
+
|
|
43
|
+
Quill automatically selects the fastest strategy for your data:
|
|
44
|
+
- Non-negative integers → narrowest-dtype numpy radix (O(n), cache-optimal)
|
|
45
|
+
- Negative integers → shift to non-negative, radix, shift back
|
|
46
|
+
- Dense int ranges → counting sort O(n+k)
|
|
47
|
+
- Floats → numpy optimised introsort
|
|
48
|
+
- Strings / bytes → Python Timsort (fastest for comparisons)
|
|
49
|
+
- Objects with key → rank-encode keys → numpy argsort
|
|
50
|
+
- Pre-sorted data → O(n) detection, immediate return
|
|
51
|
+
- Reverse-sorted → O(n) detection, single .reverse() call
|
|
52
|
+
- None values → filtered, sorted, reinserted at end
|
|
53
|
+
|
|
54
|
+
Exotic types (pandas Series/DataFrame, numpy arrays, generators) are
|
|
55
|
+
handled automatically via the plugin system — no extra code needed.
|
|
56
|
+
|
|
57
|
+
Parameters
|
|
58
|
+
----------
|
|
59
|
+
data : list
|
|
60
|
+
The list to sort. Also accepts lists of pandas objects, numpy arrays,
|
|
61
|
+
generators, or any registered plugin type.
|
|
62
|
+
key : callable, optional
|
|
63
|
+
Function applied to each element to produce a sort key.
|
|
64
|
+
For pandas DataFrames, pass a column name or list of column names.
|
|
65
|
+
reverse : bool, default False
|
|
66
|
+
If True, sort in descending order.
|
|
67
|
+
inplace : bool, default True
|
|
68
|
+
If True (default), sort the list in-place and return it.
|
|
69
|
+
If False, return a new sorted list without modifying the original.
|
|
70
|
+
parallel : bool, default False
|
|
71
|
+
Use all CPU cores. Recommended for n > 500,000 on 4+ core machines.
|
|
72
|
+
Uses shared memory (zero-copy) for integer data.
|
|
73
|
+
|
|
74
|
+
Returns
|
|
75
|
+
-------
|
|
76
|
+
list
|
|
77
|
+
The sorted list (same object if inplace=True).
|
|
78
|
+
|
|
79
|
+
Examples
|
|
80
|
+
--------
|
|
81
|
+
>>> quill_sort([3, 1, 4, 1, 5, 9])
|
|
82
|
+
[1, 1, 3, 4, 5, 9]
|
|
83
|
+
|
|
84
|
+
>>> records = [{'name': 'Bob', 'age': 25}, {'name': 'Alice', 'age': 30}]
|
|
85
|
+
>>> quill_sort(records, key=lambda r: r['age'])
|
|
86
|
+
[{'name': 'Bob', 'age': 25}, {'name': 'Alice', 'age': 30}]
|
|
87
|
+
|
|
88
|
+
>>> quill_sort([3.14, -2.71, 0.0, 1.41])
|
|
89
|
+
[-2.71, 0.0, 1.41, 3.14]
|
|
90
|
+
|
|
91
|
+
>>> quill_sort(['banana', 'apple', 'cherry'])
|
|
92
|
+
['apple', 'banana', 'cherry']
|
|
93
|
+
|
|
94
|
+
>>> quill_sort([3, None, 1, None, 2])
|
|
95
|
+
[1, 2, 3, None, None]
|
|
96
|
+
|
|
97
|
+
>>> import pandas as pd
|
|
98
|
+
>>> s = pd.Series([3, 1, 4, 1, 5])
|
|
99
|
+
>>> quill_sort(s) # returns sorted Series
|
|
100
|
+
"""
|
|
101
|
+
if not isinstance(data, list):
|
|
102
|
+
# Non-list input: try plugin system first, then wrap
|
|
103
|
+
from ._plugins import probe_plugins
|
|
104
|
+
result = probe_plugins(data, key, reverse)
|
|
105
|
+
if result is not None:
|
|
106
|
+
items, pk, postprocess = result
|
|
107
|
+
if postprocess and not items:
|
|
108
|
+
return postprocess([])
|
|
109
|
+
sorted_items = quill_sort(items, key=pk, reverse=reverse, inplace=True)
|
|
110
|
+
return postprocess(sorted_items) if postprocess else sorted_items
|
|
111
|
+
# Fall back: materialise to list
|
|
112
|
+
data = list(data)
|
|
113
|
+
|
|
114
|
+
return quill_sort_impl(data, key, reverse, inplace, parallel)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def quill_sorted(
|
|
118
|
+
iterable : Iterable,
|
|
119
|
+
key : Optional[Callable] = None,
|
|
120
|
+
reverse : bool = False,
|
|
121
|
+
parallel : bool = False,
|
|
122
|
+
) -> list:
|
|
123
|
+
"""
|
|
124
|
+
Non-mutating Quill sort — mirrors Python's built-in ``sorted()``.
|
|
125
|
+
|
|
126
|
+
Parameters
|
|
127
|
+
----------
|
|
128
|
+
iterable : any iterable
|
|
129
|
+
key : callable, optional
|
|
130
|
+
reverse : bool, default False
|
|
131
|
+
parallel : bool, default False
|
|
132
|
+
|
|
133
|
+
Returns
|
|
134
|
+
-------
|
|
135
|
+
list — a new sorted list.
|
|
136
|
+
|
|
137
|
+
Examples
|
|
138
|
+
--------
|
|
139
|
+
>>> quill_sorted(range(10, 0, -1))
|
|
140
|
+
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
|
141
|
+
|
|
142
|
+
>>> quill_sorted(['fig', 'apple', 'kiwi'], key=len)
|
|
143
|
+
['fig', 'kiwi', 'apple']
|
|
144
|
+
"""
|
|
145
|
+
return quill_sort(list(iterable), key=key, reverse=reverse,
|
|
146
|
+
inplace=True, parallel=parallel)
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"""
|
|
2
|
+
quill/__main__.py
|
|
3
|
+
-----------------
|
|
4
|
+
Entry point for `python -m quill` and the `quill` CLI command.
|
|
5
|
+
Runs the demo.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import random, time, sys
|
|
9
|
+
from . import quill_sort, __version__
|
|
10
|
+
|
|
11
|
+
try:
|
|
12
|
+
import numpy as np
|
|
13
|
+
_NUMPY = True
|
|
14
|
+
except ImportError:
|
|
15
|
+
_NUMPY = False
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def main():
|
|
19
|
+
def typewrite(text, delay=0.015):
|
|
20
|
+
for ch in text:
|
|
21
|
+
sys.stdout.write(ch); sys.stdout.flush(); time.sleep(delay)
|
|
22
|
+
print()
|
|
23
|
+
|
|
24
|
+
def bar(filled, total, width=38):
|
|
25
|
+
n = int(width * min(filled, total) / total)
|
|
26
|
+
return f"[{'█' * n}{'░' * (width - n)}]"
|
|
27
|
+
|
|
28
|
+
print()
|
|
29
|
+
typewrite(" ▄▀▄ █ █ █ █ █ ")
|
|
30
|
+
typewrite(f" ▀▄▀ ▀▄█ █ █▄▄ █▄▄ v{__version__}")
|
|
31
|
+
print()
|
|
32
|
+
typewrite(" Adaptive Ultra-Sort — by Invariant Games", 0.01)
|
|
33
|
+
np_tag = "numpy ✓ vectorised C path active" if _NUMPY \
|
|
34
|
+
else "numpy ✗ install for max speed: pip install numpy"
|
|
35
|
+
typewrite(f" [{np_tag}]", 0.008)
|
|
36
|
+
print()
|
|
37
|
+
time.sleep(0.3)
|
|
38
|
+
|
|
39
|
+
SIZES = [100, 1_000, 10_000, 100_000, 500_000,
|
|
40
|
+
1_000_000, 5_000_000, 10_000_000]
|
|
41
|
+
|
|
42
|
+
typewrite(" Benchmarking across dataset sizes...", 0.012)
|
|
43
|
+
print()
|
|
44
|
+
time.sleep(0.2)
|
|
45
|
+
|
|
46
|
+
results = []
|
|
47
|
+
for size in SIZES:
|
|
48
|
+
if size >= 1_000_000:
|
|
49
|
+
sys.stdout.write(f" {size:>12,} elements [allocating...]")
|
|
50
|
+
sys.stdout.flush()
|
|
51
|
+
|
|
52
|
+
try:
|
|
53
|
+
data = [random.randint(0, size * 10) for _ in range(size)]
|
|
54
|
+
except MemoryError:
|
|
55
|
+
sys.stdout.write(f"\r {size:>12,} elements [skipped — not enough RAM]\n")
|
|
56
|
+
continue
|
|
57
|
+
|
|
58
|
+
t0 = time.perf_counter()
|
|
59
|
+
quill_sort(data)
|
|
60
|
+
t1 = time.perf_counter()
|
|
61
|
+
elapsed = t1 - t0
|
|
62
|
+
|
|
63
|
+
correct = all(data[i] <= data[i+1] for i in range(min(len(data)-1, 50_000)))
|
|
64
|
+
results.append((size, elapsed, correct))
|
|
65
|
+
del data
|
|
66
|
+
|
|
67
|
+
label = f" {size:>12,} elements"
|
|
68
|
+
timing = f"{elapsed:>8.4f}s"
|
|
69
|
+
status = "✓" if correct else "✗"
|
|
70
|
+
b = bar(elapsed, 3.0)
|
|
71
|
+
line = f"{label} {b} {timing} {status}"
|
|
72
|
+
if size >= 1_000_000:
|
|
73
|
+
sys.stdout.write(f"\r{line}\n")
|
|
74
|
+
else:
|
|
75
|
+
print(line)
|
|
76
|
+
sys.stdout.flush()
|
|
77
|
+
time.sleep(0.04)
|
|
78
|
+
|
|
79
|
+
print()
|
|
80
|
+
time.sleep(0.3)
|
|
81
|
+
|
|
82
|
+
if len(results) >= 2:
|
|
83
|
+
s2, t2, _ = results[-1]
|
|
84
|
+
est_1b = t2 * (1_000_000_000 / s2)
|
|
85
|
+
est_str = f"{est_1b/60:.1f} minutes" if est_1b >= 60 else f"{est_1b:.1f}s"
|
|
86
|
+
typewrite(f" Quill can sort 1 billion integers in an estimated ~{est_str}.", 0.012)
|
|
87
|
+
typewrite(f" This demo is capped at 10 million due to sandbox memory constraints.", 0.012)
|
|
88
|
+
print()
|
|
89
|
+
time.sleep(0.3)
|
|
90
|
+
|
|
91
|
+
typewrite(" Watching Quill sort 20 numbers in real time...", 0.012)
|
|
92
|
+
print()
|
|
93
|
+
time.sleep(0.2)
|
|
94
|
+
|
|
95
|
+
sample = random.sample(range(1, 999), 20)
|
|
96
|
+
print(f" Before: {sample}")
|
|
97
|
+
time.sleep(0.6)
|
|
98
|
+
quill_sort(sample)
|
|
99
|
+
sys.stdout.write(" After: ")
|
|
100
|
+
sys.stdout.flush()
|
|
101
|
+
for i, v in enumerate(sample):
|
|
102
|
+
sys.stdout.write(("" if i == 0 else ", ") + str(v))
|
|
103
|
+
sys.stdout.flush()
|
|
104
|
+
time.sleep(0.055)
|
|
105
|
+
print(); print()
|
|
106
|
+
time.sleep(0.4)
|
|
107
|
+
|
|
108
|
+
big = results[-1]
|
|
109
|
+
typewrite(f" {big[0]:,} integers sorted in {big[1]:.4f}s.", 0.012)
|
|
110
|
+
typewrite( " Narrowest-dtype numpy path. Zero comparisons. Cache-optimal.", 0.012)
|
|
111
|
+
print()
|
|
112
|
+
typewrite( " pip install quill-sort", 0.022)
|
|
113
|
+
print()
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
if __name__ == "__main__":
|
|
117
|
+
main()
|