Skip to content

Commit

Permalink
Merge pull request #1 from nivlekp/skeleton
Browse files Browse the repository at this point in the history
Drafts state ideas
  • Loading branch information
nivlekp authored Aug 11, 2024
2 parents 68cc5d7 + ce863ca commit 2abf675
Show file tree
Hide file tree
Showing 8 changed files with 239 additions and 4 deletions.
68 changes: 68 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Github workflow to test compositions
name: build

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

env:
LILYPOND_VERSION: 2.25.16
PANG_COMMIT: b5dc6468810789ed8cf79136a7f0a31932904400
PANG_PATH: /tmp/pang

jobs:
build:

runs-on: ubuntu-latest
strategy:
matrix:
python-version: [ "3.12" ]

steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install LilyPond
run: |
lilypond_archive=lilypond-${LILYPOND_VERSION}-linux-x86_64.tar.gz
lilypond_url=https://gitlab.com/lilypond/lilypond/-/releases
lilypond_url=${lilypond_url}/v${LILYPOND_VERSION}/downloads/${lilypond_archive}
echo ${lilypond_url}
cd /tmp
wget -q ${lilypond_url}
tar -xf ${lilypond_archive}
- name: Upgrade pip
run: |
python -m pip install --upgrade pip coverage
- name: Log environment variable(s)
run: |
echo $PATH
- name: Install Pang
run: |
git clone https://github.com/nivlekp/pang.git ${PANG_PATH}
cd ${PANG_PATH}
git checkout ${PANG_COMMIT}
pip install ${PANG_PATH}
- name: Install dependencies
run: |
export PATH=/tmp/lilypond-${LILYPOND_VERSION}/bin:/home/runner/bin:$PATH
python -m pip install -e .[test]
black --version
flake8 --version
isort --version
pip --version
pytest --version
lilypond --version
- name: Checks and Tests
run: |
export MYPYPATH=$MYPYPATH:${PANG_PATH}
export PATH=/tmp/lilypond-${LILYPOND_VERSION}/bin:/home/runner/bin:$PATH
make black-check
make flake8
make isort-check
make pytest
make mypy
3 changes: 3 additions & 0 deletions minamidera/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from . import library, states

__all__ = ["library", "states"]
14 changes: 14 additions & 0 deletions minamidera/library.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import dataclasses
import enum

AXIS = enum.Enum("AXIS", ["FREQUENCY", "INTENSITY", "DENSITY"])
FREQUENCY_REGIONS = enum.Enum("FREQUENCY_REGIONS", ["F0", "F1"])
INTENSITY_REGIONS = enum.Enum("INTENSITY_REGIONS", ["G0", "G1"])
DENSITY_REGIONS = enum.Enum("DENSITY_REGIONS", ["D0", "D1"])


@dataclasses.dataclass
class State:
frequency_region: FREQUENCY_REGIONS
intensity_region: INTENSITY_REGIONS
density_region: DENSITY_REGIONS
15 changes: 15 additions & 0 deletions minamidera/statemapper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from collections.abc import Iterable

import numpy.typing as npt
import pang


def map_state_sequence(state_sequence: Iterable[npt.NDArray]) -> pang.Sequence:
sequence = pang.Sequence([], 0)
for state in state_sequence:
sequence.extend(map_state(state))
return sequence


def map_state(state: npt.NDArray) -> pang.Sequence:
raise NotImplementedError
73 changes: 73 additions & 0 deletions minamidera/states.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import itertools

import numpy as np
import numpy.typing as npt

STOCHASTIC_MATRIX_0 = np.array([[0.2, 0.8], [0.8, 0.2]])
STOCHASTIC_MATRIX_1 = np.array([[0.85, 0.15], [0.4, 0.6]])
STOCHASTIC_MATRICES = [STOCHASTIC_MATRIX_0, STOCHASTIC_MATRIX_1]


def enumerate_state_vectors(number_of_state_variables, number_of_discrete_state_values):
return {
key: value
for key, value in enumerate(
itertools.product(
range(number_of_discrete_state_values), repeat=number_of_state_variables
)
)
}


def generate_state_sequences(
initial_states: tuple[npt.ArrayLike, ...],
sequence_length: int,
random_number_generator: np.random.Generator,
) -> tuple[tuple[npt.NDArray, ...], ...]:
return tuple(
generate_state_sequence(initial_state, sequence_length, random_number_generator)
for initial_state in initial_states
)


def generate_state_sequence(
initial_state: npt.ArrayLike,
sequence_length: int,
random_number_generator: np.random.Generator,
) -> tuple[npt.NDArray, ...]:
assert sequence_length > 1
state_sequence = [np.array(initial_state)]
state = initial_state
for _ in range(sequence_length - 1):
state = get_next_state(state, random_number_generator)
state_sequence.append(state)
return tuple(state_sequence)


def get_next_state(
state: npt.ArrayLike, random_number_generator: np.random.Generator
) -> npt.NDArray:
state_vector = np.array(state)
assert state_vector.ndim == 1
probabilities = _compute_probabilities_for_state_vector(state_vector)
return np.array(
[random_number_generator.choice([0, 1], p=p) for p in probabilities]
)


def _compute_probabilities_for_state_vector(state: npt.NDArray) -> list[npt.NDArray]:
return [
_compute_probability_for_one_state_variable(state, state_index)
for state_index in range(len(state))
]


def _compute_probability_for_one_state_variable(state, state_index) -> npt.NDArray:
stochastic_matrix = np.zeros(STOCHASTIC_MATRIX_0.shape)
for index in range(len(state)):
if index == state_index:
continue
# a deterministic math expression that evaluates to either 0 or 1
matrix_index = (state[index] + (state_index - index) % 3) % 2
stochastic_matrix += STOCHASTIC_MATRICES[matrix_index] / (len(state) - 1)
return stochastic_matrix[state[state_index]]
7 changes: 7 additions & 0 deletions mypy.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[mypy]

[mypy-ply]
ignore_missing_imports = True

[mypy-roman]
ignore_missing_imports = True
12 changes: 8 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,23 @@
requires = ["hatchling"]
build-backend = "hatchling.build"

[tool.hatch.metadata]
allow-direct-references = true

[project]
name = "minamidera"
version = "24.6b0"
version = "24.8b0"
authors = [
{ name="Tsz Kiu Pang" },
]
description = "Minamidera, for solo piano"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
"abjad",
"abjad-ext-nauert",
"abjad @ git+https://github.com/Abjad/abjad.git@7bca4dc7ab9b55441c3196e6e6d5b9e9184cc36f",
"abjad-ext-nauert @ git+https://github.com/Abjad/abjad-ext-nauert.git@8dec05b66f537fba9ec153742658c05694c3aaec",
"pang",
"numpy >= 1.26.0, < 2.0"
"numpy >= 2.0"
]
classifiers = [
"Programming Language :: Python :: 3",
Expand All @@ -27,6 +30,7 @@ test = [
"black",
"flake8",
"isort",
"mypy",
"pytest",
]

Expand Down
51 changes: 51 additions & 0 deletions tests/test_states.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import numpy as np

from minamidera import states


def test_getting_next_state():
state = (0, 0, 1, 1)
next_state = states.get_next_state(state, np.random.default_rng())
assert len(next_state) == 4


def test_generating_state_sequence():
initial_state = (0, 0, 0, 0)
sequence_length = 100
state_sequence = states.generate_state_sequence(
initial_state, sequence_length, np.random.default_rng()
)
assert len(state_sequence) == sequence_length


def test_generating_state_sequences():
initial_state = (0, 0, 0, 0)
number_of_state_vectors = 100
initial_states = tuple(initial_state for _ in range(number_of_state_vectors))
sequence_length = 10
state_sequences = states.generate_state_sequences(
initial_states, sequence_length, np.random.default_rng()
)
assert len(state_sequences) == number_of_state_vectors
assert all([len(sequence) == sequence_length for sequence in state_sequences])


def test_enumerating_state_vectors():
assert states.enumerate_state_vectors(4, 2) == {
0: (0, 0, 0, 0),
1: (0, 0, 0, 1),
2: (0, 0, 1, 0),
3: (0, 0, 1, 1),
4: (0, 1, 0, 0),
5: (0, 1, 0, 1),
6: (0, 1, 1, 0),
7: (0, 1, 1, 1),
8: (1, 0, 0, 0),
9: (1, 0, 0, 1),
10: (1, 0, 1, 0),
11: (1, 0, 1, 1),
12: (1, 1, 0, 0),
13: (1, 1, 0, 1),
14: (1, 1, 1, 0),
15: (1, 1, 1, 1),
}

0 comments on commit 2abf675

Please sign in to comment.