Count the number of one-row extensions of Latin rectangles.
Project description
Latin Rectangles Extension Counter
A high-performance Python library for counting the number of ways to extend a 2×n Latin rectangle to a 3×n Latin rectangle using rook polynomial methods and cycle decomposition theory.
Overview
A Latin rectangle is an r×n array filled with n different symbols such that each symbol occurs exactly once in each row and at most once in each column.
Extension Problem
Given a 2×n Latin rectangle:
1 2 3 4 5 6 7 8
p[1] p[2] p[3] p[4] p[5] p[6] p[7] p[8]
where p is a derangement, the problem is to count how many valid third rows can be added such that the resulting 3×n rectangle remains a Latin rectangle. This library provides an efficient algorithm for computing Latin rectangle extensions.
Key Features
- High Performance: Approximate O(n^2) time complexity, tested up to n=800.
- Memory Efficient: Approximate O(n^1.36) memory complexity
- Mathematically Rigorous: Based on rook polynomial theory and cycle decomposition
- Easy to Use: Simple command-line interface and Python API
- Well Tested: Comprehensive test suite with complexity analysis
Installation
git clone https://github.com/ionmich/latin-rectangles.git
cd latin-rectangles
Quick Start
Command Line (CLI) Usage
Generate random derangement:
> uv run python -m latin_rectangles --n 42
🎲 Generated Random Derangement for n=42
📊 Cycle structure: [2, 2, 4, 8, 26]
🔢 Number of extensions: 185,566,788,772,996,286,199,647,931,971,186,844,003,087,641,029,824
Use specific cycle structure:
> uv run python -m latin_rectangles --c "2,2,4"
⚙️ Specific Cycle Structure for n=8
📊 Cycle structure: [2, 2, 4]
🔢 Number of extensions: 4,744
Enumerate all possible cycle structures:
> uv run python -m latin_rectangles --n 8 --all
🔍 All Cycle Structures for n=8
📊 Found 7 possible structures with non-zero extensions:
1. [2, 2, 2, 2] → 4,752 extensions
2. [2, 2, 4] → 4,744 extensions
3. [2, 3, 3] → 4,740 extensions
4. [2, 6] → 4,740 extensions
5. [4, 4] → 4,740 extensions
6. [3, 5] → 4,738 extensions
7. [8] → 4,738 extensions
Get help
uv run latin-rectangles --help
Python Library Usage
from latin_rectangles import count_extensions, count_random_extensions
from latin_rectangles import generate_random_derangement
# Method 1: One-liner for random derangement
extensions = count_random_extensions(n=12)
print(f"Extensions: {extensions:,}")
# Method 2: Step-by-step with custom derangement
derangement = generate_random_derangement(n=10)
extensions = count_extensions(derangement)
print(f"Derangement {derangement[1:]} has {extensions:,} extensions")
# Method 3: With predefined derangement (1-indexed with dummy 0)
p = [0, 2, 3, 4, 5, 6, 7, 8, 1] # 8-cycle for n=8
extensions = count_extensions(p)
print(f"8-cycle has {extensions:,} extensions") # Output: 4,738
Algorithm Details
Mathematical Foundation
The algorithm leverages rook polynomial theory to solve the Latin rectangle extension problem:
- Input: A derangement (permutation with no fixed points) representing the second row
- Cycle Decomposition: Decompose the derangement into disjoint cycles
- Rook Polynomials: Compute rook polynomial for each cycle structure
- Polynomial Multiplication: Combine rook polynomials to get the final count
API Reference
Core Functions
count_extensions(permutation: list[int]) -> int
Counts the number of extensions for a given derangement.
Parameters:
permutation: 1-indexed list representing a derangement (p[0] is dummy value)
Returns: Integer number of possible third rows
Raises: ValueError if input is not a derangement
count_random_extensions(n: int) -> int
Convenience function that generates a random derangement and counts its extensions.
Parameters:
n: Size of the derangement (must be > 1)
Returns: Number of extensions for the randomly generated derangement
generate_random_derangement(n: int) -> list[int]
Generates a random derangement of size n.
Parameters:
n: Size of the derangement
Returns: 1-indexed list representing the derangement
find_cycle_decomposition(permutation: list[int]) -> list[list[int]]
Finds the cycle decomposition of a permutation.
Parameters:
permutation: 1-indexed permutation
Returns: List of cycles (each cycle is a list of indices)
Examples
Basic Usage Examples
from latin_rectangles import count_extensions
# Example 1: Single 8-cycle
p_8_cycle = [0, 2, 3, 4, 5, 6, 7, 8, 1]
print(f"8-cycle: {count_extensions(p_8_cycle):,} extensions")
# Output: 8-cycle: 4,738 extensions
# Example 2: Two 4-cycles
p_4_4 = [0, 2, 3, 4, 1, 6, 7, 8, 5]
print(f"4,4-cycles: {count_extensions(p_4_4):,} extensions")
# Output: 4,4-cycles: 4,740 extensions
# Example 3: Four 2-cycles
p_2_2_2_2 = [0, 2, 1, 4, 3, 6, 5, 8, 7]
print(f"2,2,2,2-cycles: {count_extensions(p_2_2_2_2):,} extensions")
# Output: 2,2,2,2-cycles: 4,752 extensions
Advanced Usage
from latin_rectangles import generate_random_derangement, find_cycle_decomposition, count_extensions
# Generate and analyze a random derangement
n = 15
derangement = generate_random_derangement(n)
cycles = find_cycle_decomposition(derangement)
cycle_lengths = sorted([len(c) for c in cycles])
extensions = count_extensions(derangement)
print(f"n={n}")
print(f"Derangement: {derangement[1:]}")
print(f"Cycle structure: {cycle_lengths}")
print(f"Extensions: {extensions:,}")
Batch Processing
from latin_rectangles import count_random_extensions
# Process multiple sizes
results = []
for n in range(5, 21):
extensions = count_random_extensions(n)
results.append((n, extensions))
print(f"n={n:2d}: {extensions:,} extensions")
# Find the size with the most extensions in this batch
max_n, max_extensions = max(results, key=lambda x: x[1])
print(f"Maximum: n={max_n} with {max_extensions:,} extensions")
Development
Running Tests
# Run the test suite
uv run pytest
# Run with coverage
uv run pytest --cov=latin_rectangles
# Run specific test
uv run pytest tests/test_main.py -v
Code Quality
# Type checking
uv run mypy src/
# Linting
uv run ruff check src/
# Formatting
uv run ruff format src/
Benchmarking
# Run performance benchmarks
uv run python benchmark.py
# Analyze complexity
uv run python complexity_analysis.py
Contributing
Contributions are welcome! Please see DEVELOPMENT.md for development guidelines.
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Ensure all tests pass
- Submit a pull request
License
This project is licensed under the MIT License - see the LICENSE file for details.
Citation
If you use this library in your research, please cite:
@software{latin_rectangles,
title={Latin Rectangles Extension Counter},
author={Ioannis Michaloliakos},
year={2025},
url={https://github.com/ionmich/latin-rectangles}
}
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file latin_rectangles-0.1.0.tar.gz.
File metadata
- Download URL: latin_rectangles-0.1.0.tar.gz
- Upload date:
- Size: 422.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.8.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ee690ef452b786ed65674f6b9359ecd7b76c8a128177d20937ee3ae604431d36
|
|
| MD5 |
061c354f103c3023f760ffcebc57111d
|
|
| BLAKE2b-256 |
183eec4b1afe519d3656b0bd160d961647fec2fe21e0ae5bf5362ea0f01ed444
|
File details
Details for the file latin_rectangles-0.1.0-py3-none-any.whl.
File metadata
- Download URL: latin_rectangles-0.1.0-py3-none-any.whl
- Upload date:
- Size: 11.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.8.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1cf83d13eb2b42bc2e959b82e211721cbfe3492def5cf4fcbb7d63bdfc22cafb
|
|
| MD5 |
1a5b7538c3e00e4598c701879961e016
|
|
| BLAKE2b-256 |
e2cb49725202c3fcbaf6f1b5bd7744a77ae099ab88152897abe0932af68e0cfe
|