Skip to content

Commit 7a72a50

Browse files
author
Aaron Meyer
committed
Migrate to uv, ty, and ruff; simplify and fix typing issues
1 parent 2aa19a3 commit 7a72a50

72 files changed

Lines changed: 3650 additions & 3174 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/workflows/build.yml‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ jobs:
77
runs-on: self-hosted
88
steps:
99
- uses: actions/checkout@v4
10+
- name: Install uv
11+
uses: astral-sh/setup-uv@v5
1012
- name: Install dependencies
11-
run: poetry install
13+
run: uv sync
1214
- name: Build figures
1315
run: make all -j 12
1416
- name: Upload files

‎.github/workflows/pytest.yml‎

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
2-
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
3-
41
name: Test
52

63
on: [push]
@@ -12,10 +9,18 @@ jobs:
129
- uses: actions/checkout@v4
1310
- name: Setup submodule
1411
run: git submodule update --init --recursive
12+
- name: Install uv
13+
uses: astral-sh/setup-uv@v5
1514
- name: Install dependencies
16-
run: poetry install
15+
run: uv sync
16+
- name: Lint with ruff
17+
run: uv run ruff check .
18+
- name: Check formatting with ruff
19+
run: uv run ruff format --check .
20+
- name: Type check with ty
21+
run: uv run ty check
1722
- name: Test with pytest
18-
run: poetry run pytest --cov=lineage --cov-report=xml --cov-config=.github/workflows/coveragerc
23+
run: uv run pytest --cov=lineage --cov-report=xml --cov-config=.github/workflows/coveragerc
1924
- name: Upload coverage to Codecov
2025
uses: codecov/codecov-action@v4
2126
with:

‎.gitignore‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@
44
**/.pytest_cache
55
**/__pycache__/
66
nosetests.xml
7-
**/.mypy_cache
7+
**/.ruff_cache
88

99
# Notebooks
1010
**.ipynb
1111
# Exception to the rule
1212
!/notebooks/*.ipynb
1313

14-
14+
.venv/
1515
venv/
1616
profile
1717
profile.svg
@@ -21,3 +21,5 @@ spell.txt
2121
.vscode/settings.json
2222
.vscode
2323
lineage/data/taxol
24+
output/
25+

‎README.rst‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,13 @@ This package is supported on *macOS* and *Linux* systems.
2929
Python dependencies
3030
~~~~~~~~~~~~~~~~~~~
3131

32-
``tHMM`` requires ``virtualenv``. All other required packages can then be installed using ``make venv`` to establish a virtual environment.
33-
The Python packages that will be installed are listed in pyproject.toml, and can be directly installed using ``poetry install``.
32+
Dependencies are managed with `uv <https://docs.astral.sh/uv/>`_.
33+
Install the environment and dependencies by running:
34+
35+
.. code-block:: console
36+
37+
$ uv sync
38+
3439
3540
3641
Documentation

‎docs/conf.py‎

Lines changed: 29 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -10,75 +10,73 @@
1010
# add these directories to sys.path here. If the directory is relative to the
1111
# documentation root, use os.path.abspath to make it absolute, like shown here.
1212
#
13-
from sphinx import addnodes
14-
import re
1513
import os
14+
import re
1615
import sys
17-
sys.path.insert(0, os.path.abspath('..'))
16+
17+
from sphinx import addnodes
18+
19+
sys.path.insert(0, os.path.abspath(".."))
1820

1921

2022
# -- Project information -----------------------------------------------------
2123

22-
project = 'tHMM'
23-
copyright = '2022, Farnaz Mohammadi, JC Lagarde, Aaron S Meyer'
24-
author = 'Farnaz Mohammadi, JC Lagarde, Aaron S Meyer'
24+
project = "tHMM"
25+
copyright = "2022, Farnaz Mohammadi, JC Lagarde, Aaron S Meyer"
26+
author = "Farnaz Mohammadi, JC Lagarde, Aaron S Meyer"
2527

2628
# The full version, including alpha/beta/rc tags
27-
release = '1.0'
29+
release = "1.0"
2830

2931

3032
# -- General configuration ---------------------------------------------------
3133

3234
# Add any Sphinx extension module names here, as strings. They can be
3335
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
3436
# ones.
35-
extensions = [
36-
'sphinx.ext.autodoc',
37-
'autoapi.extension',
38-
'sphinx_rtd_theme'
39-
]
40-
autodoc_mock_imports = ['bs4', 'requests']
41-
42-
autoapi_dirs = ['../lineage']
37+
extensions = ["sphinx.ext.autodoc", "autoapi.extension", "sphinx_rtd_theme"]
38+
autodoc_mock_imports = ["bs4", "requests"]
39+
40+
autoapi_dirs = ["../lineage"]
4341
autoapi_ignore = ["*/test_*.py", "*/figure*.py"]
44-
autoapi_type = 'python'
42+
autoapi_type = "python"
4543

4644
# Add any paths that contain templates here, relative to this directory.
47-
templates_path = ['_templates']
45+
templates_path = ["_templates"]
4846

4947
# The language for content autogenerated by Sphinx. Refer to documentation
5048
# for a list of supported languages.
5149
#
5250
# This is also used if you do content translation via gettext catalogs.
5351
# Usually you set "language" from the command line for these cases.
54-
language = 'python'
52+
language = "python"
5553

5654
# autoapi_generate_api_docs = False
5755

5856
# List of patterns, relative to source directory, that match files and
5957
# directories to ignore when looking for source files.
6058
# This pattern also affects html_static_path and html_extra_path.
61-
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
59+
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
6260

6361

6462
# -- Options for HTML output -------------------------------------------------
6563

6664
# The theme to use for HTML and HTML Help pages. See the documentation for
6765
# a list of builtin themes.
6866
#
69-
html_theme = 'sphinx_rtd_theme'
67+
html_theme = "sphinx_rtd_theme"
7068

7169
# Add any paths that contain custom static files (such as style sheets) here,
7270
# relative to this directory. They are copied after the builtin static files,
7371
# so a file named "default.css" will overwrite the builtin "default.css".
74-
html_static_path = ['_static']
72+
html_static_path = ["_static"]
7573

76-
autoapi_root = 'technical/api'
74+
autoapi_root = "technical/api"
7775

78-
autodoc_typehints = 'description'
76+
autodoc_typehints = "description"
7977

8078

81-
event_sig_re = re.compile(r'([a-zA-Z-]+)\s*\((.*)\)')
79+
event_sig_re = re.compile(r"([a-zA-Z-]+)\s*\((.*)\)")
8280

8381

8482
def parse_event(env, sig, signode):
@@ -89,7 +87,7 @@ def parse_event(env, sig, signode):
8987
name, args = m.groups()
9088
signode += addnodes.desc_name(name, name)
9189
plist = addnodes.desc_parameterlist()
92-
for arg in args.split(','):
90+
for arg in args.split(","):
9391
arg = arg.strip()
9492
plist += addnodes.desc_parameter(arg, arg)
9593
signode += plist
@@ -98,10 +96,9 @@ def parse_event(env, sig, signode):
9896

9997
def setup(app):
10098
from sphinx.util.docfields import TypedField
101-
app.add_object_type('confval', 'confval',
102-
objname='configuration value',
103-
indextemplate='pair: %s; configuration value')
104-
fdesc = TypedField('parameter', label='Parameters',
105-
names=['param'], typenames=['type'], can_collapse=True)
106-
app.add_object_type('event', 'event', 'pair: %s; event', parse_event,
107-
doc_field_types=[fdesc])
99+
100+
app.add_object_type(
101+
"confval", "confval", objname="configuration value", indextemplate="pair: %s; configuration value"
102+
)
103+
fdesc = TypedField("parameter", label="Parameters", names=["param"], typenames=["type"], can_collapse=True)
104+
app.add_object_type("event", "event", "pair: %s; event", parse_event, doc_field_types=[fdesc])

‎fit_distribution.py‎

Lines changed: 0 additions & 41 deletions
This file was deleted.

‎lineage/Analyze.py‎

Lines changed: 22 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,25 @@
1-
""" Calls the tHMM functions and outputs the parameters needed to generate the Figures. """
1+
"""Calls the tHMM functions and outputs the parameters needed to generate the Figures."""
22

33
import itertools
4-
from typing import Any, Tuple
4+
from typing import Any
5+
56
import numpy as np
67
from scipy.optimize import linear_sum_assignment
78
from sklearn.metrics import rand_score
8-
from .tHMM import tHMM
9+
910
from .BaumWelch import (
10-
do_E_step,
1111
calculate_log_likelihood,
12-
do_M_step,
12+
do_E_step,
1313
do_M_E_step,
1414
do_M_E_step_atonce,
15+
do_M_step,
1516
)
17+
from .tHMM import tHMM
1618

1719

1820
def fit_list(
1921
tHMMobj_list: list[tHMM], tolerance: float = 1e-6, max_iter: int = 200, rng=None
20-
) -> Tuple[list[np.ndarray], list[np.ndarray], float]:
22+
) -> tuple[list[list[np.ndarray]], list[list[list[np.ndarray]]], float]:
2123
"""
2224
Runs the tHMM function through Baum Welch fitting for a list containing a set of data for different concentrations.
2325
@@ -34,10 +36,7 @@ def fit_list(
3436

3537
# Step 0: initialize with random assignments and do an M step
3638
# when there are no fixed emissions, we need to randomize the start
37-
init_gam = [
38-
[rng.dirichlet(np.ones(tO.num_states), size=len(lin)) for lin in tO.X]
39-
for tO in tHMMobj_list
40-
]
39+
init_gam = [[rng.dirichlet(np.ones(tO.num_states), size=len(lin)) for lin in tO.X] for tO in tHMMobj_list]
4140

4241
if len(tHMMobj_list) > 1: # it means we are fitting several concentrations at once.
4342
do_M_E_step_atonce(tHMMobj_list, init_gam)
@@ -46,15 +45,15 @@ def fit_list(
4645

4746
# Step 1: first E step
4847
MSD_list, NF_list, betas_list, gammas_list = map(
49-
list, zip(*[do_E_step(tHMM) for tHMM in tHMMobj_list])
48+
list, zip(*[do_E_step(tHMM) for tHMM in tHMMobj_list], strict=False)
5049
)
5150
old_LL = calculate_log_likelihood(NF_list)
5251

5352
# first stopping condition check
5453
for _ in range(max_iter):
5554
do_M_step(tHMMobj_list, MSD_list, betas_list, gammas_list)
5655
MSD_list, NF_list, betas_list, gammas_list = map(
57-
list, zip(*[do_E_step(tHMM) for tHMM in tHMMobj_list])
56+
list, zip(*[do_E_step(tHMM) for tHMM in tHMMobj_list], strict=False)
5857
)
5958
new_LL = calculate_log_likelihood(NF_list)
6059
if new_LL - old_LL < tolerance:
@@ -67,7 +66,7 @@ def fit_list(
6766

6867
def Analyze_list(
6968
pop_list: list, num_states: int, fpi=None, fT=None, rng=None, write_states=False
70-
) -> Tuple[list[tHMM], float, list[np.ndarray]]:
69+
) -> tuple[list[tHMM], float, list[list[list[np.ndarray]]]]:
7170
"""This function runs the analyze function for the case when we want to fit multiple conditions at the same time.
7271
:param pop_list: The list of cell populations to run the analyze function on.
7372
:param num_states: The number of states that we want to run the model for.
@@ -110,7 +109,7 @@ def run_Analyze_over(
110109
atonce=False,
111110
list_of_fpi=None,
112111
list_of_fT=None,
113-
) -> list[Tuple[list[tHMM], float, list[np.ndarray]]]:
112+
) -> list[tuple[list[tHMM], float, list[np.ndarray]]]:
114113
"""
115114
A function that can be parallelized to speed up figure creation.
116115
@@ -180,37 +179,23 @@ def Results(tHMMobj: tHMM, LL: float) -> dict[str, Any]:
180179

181180
true_states_by_lineage = [lin.states for lin in tHMMobj.X]
182181

183-
results_dict["transition_matrix_similarity"] = np.linalg.norm(
184-
tHMMobj.estimate.T - tHMMobj.X[0].T
185-
)
182+
results_dict["transition_matrix_similarity"] = np.linalg.norm(tHMMobj.estimate.T - tHMMobj.X[0].T)
186183

187-
results_dict["pi_similarity"] = np.linalg.norm(
188-
tHMMobj.X[0].pi - tHMMobj.estimate.pi
189-
)
184+
results_dict["pi_similarity"] = np.linalg.norm(tHMMobj.X[0].pi - tHMMobj.estimate.pi)
190185

191186
# Get the estimated parameter values
192-
results_dict["param_estimates"] = [
193-
tHMMobj.estimate.E[x].params for x in range(tHMMobj.num_states)
194-
]
187+
results_dict["param_estimates"] = [tHMMobj.estimate.E[x].params for x in range(tHMMobj.num_states)]
195188

196189
# Get the true parameter values
197-
results_dict["param_trues"] = [
198-
tHMMobj.X[0].E[x].params for x in range(tHMMobj.num_states)
199-
]
190+
results_dict["param_trues"] = [tHMMobj.X[0].E[x].params for x in range(tHMMobj.num_states)]
200191

201192
# Get the distance between distributions of two states
202-
results_dict["distribution distance 0"] = tHMMobj.estimate.E[0].dist(
203-
tHMMobj.X[0].E[0]
204-
)
205-
results_dict["distribution distance 1"] = tHMMobj.estimate.E[1].dist(
206-
tHMMobj.X[0].E[1]
207-
)
193+
results_dict["distribution distance 0"] = tHMMobj.estimate.E[0].dist(tHMMobj.X[0].E[0])
194+
results_dict["distribution distance 1"] = tHMMobj.estimate.E[1].dist(tHMMobj.X[0].E[1])
208195

209196
# 2. Calculate accuracy after switching states
210197
results_dict["state_counter"] = np.bincount(pred_states[0])
211-
results_dict["state_proportions"] = [
212-
100.0 * i / len(pred_states[0]) for i in results_dict["state_counter"]
213-
]
198+
results_dict["state_proportions"] = [100.0 * i / len(pred_states[0]) for i in results_dict["state_counter"]]
214199
results_dict["state_proportions_0"] = results_dict["state_proportions"][0]
215200
results_dict["state_similarity"] = 100.0 * rand_score(
216201
list(itertools.chain(*true_states_by_lineage)),
@@ -223,7 +208,7 @@ def Results(tHMMobj: tHMM, LL: float) -> dict[str, Any]:
223208
return results_dict
224209

225210

226-
def permute_states(tHMMobj: tHMM, switch_map: np.ndarray) -> Tuple[Any, list]:
211+
def permute_states(tHMMobj: tHMM, switch_map: np.ndarray) -> tuple[Any, list]:
227212
"""
228213
This function takes the tHMMobj and the predicted states,
229214
and finds out whether we need to switch the state identities or not based on the likelihood.
@@ -234,9 +219,7 @@ def permute_states(tHMMobj: tHMM, switch_map: np.ndarray) -> Tuple[Any, list]:
234219
"""
235220
pred_states = tHMMobj.predict()
236221

237-
pred_states_switched = [
238-
np.array([switch_map[st] for sublist in pred_states for st in sublist])
239-
]
222+
pred_states_switched = [np.array([switch_map[st] for sublist in pred_states for st in sublist])]
240223

241224
# Rearrange the values in the transition matrix
242225
tHMMobj.estimate.T = tHMMobj.estimate.T[switch_map, :]

0 commit comments

Comments
 (0)