Skip to content

Commit

Permalink
Merge branch 'TheAlgorithms:master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
shashankrushiya authored Dec 24, 2024
2 parents 2817073 + c36aaf0 commit 55d085a
Show file tree
Hide file tree
Showing 45 changed files with 1,855 additions and 256 deletions.
15 changes: 6 additions & 9 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,18 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v4
with:
enable-cache: true
cache-dependency-glob: uv.lock
- uses: actions/setup-python@v5
with:
python-version: 3.13
allow-prereleases: true
- uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip setuptools wheel
python -m pip install pytest-cov -r requirements.txt
- run: uv sync --group=test
- name: Run tests
# TODO: #8818 Re-enable quantum tests
run: pytest
run: uv run pytest
--ignore=computer_vision/cnn_classification.py
--ignore=docs/conf.py
--ignore=dynamic_programming/k_means_clustering_tensorflow.py
Expand Down
16 changes: 6 additions & 10 deletions .github/workflows/project_euler.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,25 +15,21 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v4
- uses: actions/setup-python@v5
with:
python-version: 3.x
- name: Install pytest and pytest-cov
run: |
python -m pip install --upgrade pip
python -m pip install --upgrade numpy pytest pytest-cov
- run: pytest --doctest-modules --cov-report=term-missing:skip-covered --cov=project_euler/ project_euler/
- run: uv sync --group=euler-validate --group=test
- run: uv run pytest --doctest-modules --cov-report=term-missing:skip-covered --cov=project_euler/ project_euler/
validate-solutions:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v4
- uses: actions/setup-python@v5
with:
python-version: 3.x
- name: Install pytest and requests
run: |
python -m pip install --upgrade pip
python -m pip install --upgrade numpy pytest requests
- run: pytest scripts/validate_solutions.py
- run: uv sync --group=euler-validate --group=test
- run: uv run pytest scripts/validate_solutions.py
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
4 changes: 2 additions & 2 deletions .github/workflows/ruff.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: pip install --user ruff
- run: ruff check --output-format=github .
- uses: astral-sh/setup-uv@v4
- run: uvx ruff check --output-format=github .
6 changes: 3 additions & 3 deletions .github/workflows/sphinx.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v4
- uses: actions/setup-python@v5
with:
python-version: 3.13
allow-prereleases: true
- run: pip install --upgrade pip
- run: pip install myst-parser sphinx-autoapi sphinx-pyproject
- run: uv sync --group=docs
- uses: actions/configure-pages@v5
- run: sphinx-build -c docs . docs/_build/html
- run: uv run sphinx-build -c docs . docs/_build/html
- uses: actions/upload-pages-artifact@v3
with:
path: docs/_build/html
Expand Down
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ repos:
- id: auto-walrus

- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.7.2
rev: v0.8.3
hooks:
- id: ruff
- id: ruff-format
Expand All @@ -42,7 +42,7 @@ repos:
pass_filenames: false

- repo: https://github.com/abravalheri/validate-pyproject
rev: v0.22
rev: v0.23
hooks:
- id: validate-pyproject

Expand Down
1 change: 1 addition & 0 deletions DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -794,6 +794,7 @@
* [Cramers Rule 2X2](matrix/cramers_rule_2x2.py)
* [Inverse Of Matrix](matrix/inverse_of_matrix.py)
* [Largest Square Area In Matrix](matrix/largest_square_area_in_matrix.py)
* [Matrix Based Game](matrix/matrix_based_game.py)
* [Matrix Class](matrix/matrix_class.py)
* [Matrix Equalization](matrix/matrix_equalization.py)
* [Matrix Multiplication Recursion](matrix/matrix_multiplication_recursion.py)
Expand Down
32 changes: 19 additions & 13 deletions audio_filters/iir_filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,17 @@ class IIRFilter:
Implementation details:
Based on the 2nd-order function from
https://en.wikipedia.org/wiki/Digital_biquad_filter,
https://en.wikipedia.org/wiki/Digital_biquad_filter,
this generalized N-order function was made.
Using the following transfer function
H(z)=\frac{b_{0}+b_{1}z^{-1}+b_{2}z^{-2}+...+b_{k}z^{-k}}{a_{0}+a_{1}z^{-1}+a_{2}z^{-2}+...+a_{k}z^{-k}}
.. math:: H(z)=\frac{b_{0}+b_{1}z^{-1}+b_{2}z^{-2}+...+b_{k}z^{-k}}
{a_{0}+a_{1}z^{-1}+a_{2}z^{-2}+...+a_{k}z^{-k}}
we can rewrite this to
y[n]={\frac{1}{a_{0}}}\left(\left(b_{0}x[n]+b_{1}x[n-1]+b_{2}x[n-2]+...+b_{k}x[n-k]\right)-\left(a_{1}y[n-1]+a_{2}y[n-2]+...+a_{k}y[n-k]\right)\right)
.. math:: y[n]={\frac{1}{a_{0}}}
\left(\left(b_{0}x[n]+b_{1}x[n-1]+b_{2}x[n-2]+...+b_{k}x[n-k]\right)-
\left(a_{1}y[n-1]+a_{2}y[n-2]+...+a_{k}y[n-k]\right)\right)
"""

def __init__(self, order: int) -> None:
Expand All @@ -34,17 +38,19 @@ def __init__(self, order: int) -> None:

def set_coefficients(self, a_coeffs: list[float], b_coeffs: list[float]) -> None:
"""
Set the coefficients for the IIR filter. These should both be of size order + 1.
a_0 may be left out, and it will use 1.0 as default value.
Set the coefficients for the IIR filter.
These should both be of size `order` + 1.
:math:`a_0` may be left out, and it will use 1.0 as default value.
This method works well with scipy's filter design functions
>>> # Make a 2nd-order 1000Hz butterworth lowpass filter
>>> import scipy.signal
>>> b_coeffs, a_coeffs = scipy.signal.butter(2, 1000,
... btype='lowpass',
... fs=48000)
>>> filt = IIRFilter(2)
>>> filt.set_coefficients(a_coeffs, b_coeffs)
>>> # Make a 2nd-order 1000Hz butterworth lowpass filter
>>> import scipy.signal
>>> b_coeffs, a_coeffs = scipy.signal.butter(2, 1000,
... btype='lowpass',
... fs=48000)
>>> filt = IIRFilter(2)
>>> filt.set_coefficients(a_coeffs, b_coeffs)
"""
if len(a_coeffs) < self.order:
a_coeffs = [1.0, *a_coeffs]
Expand All @@ -68,7 +74,7 @@ def set_coefficients(self, a_coeffs: list[float], b_coeffs: list[float]) -> None

def process(self, sample: float) -> float:
"""
Calculate y[n]
Calculate :math:`y[n]`
>>> filt = IIRFilter(2)
>>> filt.process(0)
Expand Down
6 changes: 2 additions & 4 deletions cellular_automata/conways_game_of_life.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,8 @@ def new_generation(cells: list[list[int]]) -> list[list[int]]:
# 3. All other live cells die in the next generation.
# Similarly, all other dead cells stay dead.
alive = cells[i][j] == 1
if (
(alive and 2 <= neighbour_count <= 3)
or not alive
and neighbour_count == 3
if (alive and 2 <= neighbour_count <= 3) or (
not alive and neighbour_count == 3
):
next_generation_row.append(1)
else:
Expand Down
54 changes: 26 additions & 28 deletions cellular_automata/wa_tor.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
"""
Wa-Tor algorithm (1984)
@ https://en.wikipedia.org/wiki/Wa-Tor
@ https://beltoforion.de/en/wator/
@ https://beltoforion.de/en/wator/images/wator_medium.webm
| @ https://en.wikipedia.org/wiki/Wa-Tor
| @ https://beltoforion.de/en/wator/
| @ https://beltoforion.de/en/wator/images/wator_medium.webm
This solution aims to completely remove any systematic approach
to the Wa-Tor planet, and utilise fully random methods.
Expand Down Expand Up @@ -97,8 +97,8 @@ class WaTor:
:attr time_passed: A function that is called every time
time passes (a chronon) in order to visually display
the new Wa-Tor planet. The time_passed function can block
using time.sleep to slow the algorithm progression.
the new Wa-Tor planet. The `time_passed` function can block
using ``time.sleep`` to slow the algorithm progression.
>>> wt = WaTor(10, 15)
>>> wt.width
Expand Down Expand Up @@ -216,7 +216,7 @@ def get_surrounding_prey(self, entity: Entity) -> list[Entity]:
"""
Returns all the prey entities around (N, S, E, W) a predator entity.
Subtly different to the try_to_move_to_unoccupied square.
Subtly different to the `move_and_reproduce`.
>>> wt = WaTor(WIDTH, HEIGHT)
>>> wt.set_planet([
Expand Down Expand Up @@ -260,7 +260,7 @@ def move_and_reproduce(
"""
Attempts to move to an unoccupied neighbouring square
in either of the four directions (North, South, East, West).
If the move was successful and the remaining_reproduction time is
If the move was successful and the `remaining_reproduction_time` is
equal to 0, then a new prey or predator can also be created
in the previous square.
Expand Down Expand Up @@ -351,12 +351,12 @@ def perform_prey_actions(
Performs the actions for a prey entity
For prey the rules are:
1. At each chronon, a prey moves randomly to one of the adjacent unoccupied
squares. If there are no free squares, no movement takes place.
2. Once a prey has survived a certain number of chronons it may reproduce.
This is done as it moves to a neighbouring square,
leaving behind a new prey in its old position.
Its reproduction time is also reset to zero.
1. At each chronon, a prey moves randomly to one of the adjacent unoccupied
squares. If there are no free squares, no movement takes place.
2. Once a prey has survived a certain number of chronons it may reproduce.
This is done as it moves to a neighbouring square,
leaving behind a new prey in its old position.
Its reproduction time is also reset to zero.
>>> wt = WaTor(WIDTH, HEIGHT)
>>> reproducable_entity = Entity(True, coords=(0, 1))
Expand All @@ -382,15 +382,15 @@ def perform_predator_actions(
:param occupied_by_prey_coords: Move to this location if there is prey there
For predators the rules are:
1. At each chronon, a predator moves randomly to an adjacent square occupied
by a prey. If there is none, the predator moves to a random adjacent
unoccupied square. If there are no free squares, no movement takes place.
2. At each chronon, each predator is deprived of a unit of energy.
3. Upon reaching zero energy, a predator dies.
4. If a predator moves to a square occupied by a prey,
it eats the prey and earns a certain amount of energy.
5. Once a predator has survived a certain number of chronons
it may reproduce in exactly the same way as the prey.
1. At each chronon, a predator moves randomly to an adjacent square occupied
by a prey. If there is none, the predator moves to a random adjacent
unoccupied square. If there are no free squares, no movement takes place.
2. At each chronon, each predator is deprived of a unit of energy.
3. Upon reaching zero energy, a predator dies.
4. If a predator moves to a square occupied by a prey,
it eats the prey and earns a certain amount of energy.
5. Once a predator has survived a certain number of chronons
it may reproduce in exactly the same way as the prey.
>>> wt = WaTor(WIDTH, HEIGHT)
>>> wt.set_planet([[Entity(True, coords=(0, 0)), Entity(False, coords=(0, 1))]])
Expand Down Expand Up @@ -430,7 +430,7 @@ def perform_predator_actions(

def run(self, *, iteration_count: int) -> None:
"""
Emulate time passing by looping iteration_count times
Emulate time passing by looping `iteration_count` times
>>> wt = WaTor(WIDTH, HEIGHT)
>>> wt.run(iteration_count=PREDATOR_INITIAL_ENERGY_VALUE - 1)
Expand Down Expand Up @@ -484,11 +484,9 @@ def visualise(wt: WaTor, iter_number: int, *, colour: bool = True) -> None:
an ascii code in terminal to clear and re-print
the Wa-Tor planet at intervals.
Uses ascii colour codes to colourfully display
the predators and prey.
(0x60f197) Prey = #
(0xfffff) Predator = x
Uses ascii colour codes to colourfully display the predators and prey:
* (0x60f197) Prey = ``#``
* (0xfffff) Predator = ``x``
>>> wt = WaTor(30, 30)
>>> wt.set_planet([
Expand Down
2 changes: 1 addition & 1 deletion ciphers/playfair_cipher.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from collections.abc import Generator, Iterable


def chunker(seq: Iterable[str], size: int) -> Generator[tuple[str, ...], None, None]:
def chunker(seq: Iterable[str], size: int) -> Generator[tuple[str, ...]]:
it = iter(seq)
while True:
chunk = tuple(itertools.islice(it, size))
Expand Down
2 changes: 1 addition & 1 deletion ciphers/simple_keyword_cypher.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ def remove_duplicates(key: str) -> str:

key_no_dups = ""
for ch in key:
if ch == " " or ch not in key_no_dups and ch.isalpha():
if ch == " " or (ch not in key_no_dups and ch.isalpha()):
key_no_dups += ch
return key_no_dups

Expand Down
6 changes: 2 additions & 4 deletions ciphers/transposition_cipher.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,8 @@ def decrypt_message(key: int, message: str) -> str:
plain_text[col] += symbol
col += 1

if (
(col == num_cols)
or (col == num_cols - 1)
and (row >= num_rows - num_shaded_boxes)
if (col == num_cols) or (
(col == num_cols - 1) and (row >= num_rows - num_shaded_boxes)
):
col = 0
row += 1
Expand Down
4 changes: 2 additions & 2 deletions compression/lempel_ziv.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ def add_key_to_lexicon(
lexicon[curr_string + "0"] = last_match_id

if math.log2(index).is_integer():
for curr_key in lexicon:
lexicon[curr_key] = "0" + lexicon[curr_key]
for curr_key, value in lexicon.items():
lexicon[curr_key] = f"0{value}"

lexicon[curr_string + "1"] = bin(index)[2:]

Expand Down
4 changes: 2 additions & 2 deletions data_structures/arrays/sudoku_solver.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ def time_solve(grid):
times, results = zip(*[time_solve(grid) for grid in grids])
if (n := len(grids)) > 1:
print(
"Solved %d of %d %s puzzles (avg %.2f secs (%d Hz), max %.2f secs)."
"Solved %d of %d %s puzzles (avg %.2f secs (%d Hz), max %.2f secs)." # noqa: UP031
% (sum(results), n, name, sum(times) / n, n / sum(times), max(times))
)

Expand All @@ -172,7 +172,7 @@ def unitsolved(unit):

def from_file(filename, sep="\n"):
"Parse a file into a list of strings, separated by sep."
return open(filename).read().strip().split(sep) # noqa: SIM115
return open(filename).read().strip().split(sep)


def random_puzzle(assignments=17):
Expand Down
Loading

0 comments on commit 55d085a

Please sign in to comment.