Skip to content

Commit

Permalink
Merge pull request #3 from DNA-and-Natural-Algorithms-Group/numpy2
Browse files Browse the repository at this point in the history
update versions to use rust numpy 0.22, pyo3 0.22; allow numpy 2
  • Loading branch information
cgevans authored Oct 30, 2024
2 parents f0bdd28 + 785858d commit 37ff57d
Show file tree
Hide file tree
Showing 25 changed files with 311 additions and 326 deletions.
36 changes: 18 additions & 18 deletions .github/workflows/python-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,31 +29,31 @@ jobs:
# options: "--check --verbose --diff"
# src: "./src"

mypy:
runs-on: ubuntu-latest
steps:
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: "3.12"
- uses: actions/checkout@v3
- name: Install dependencies
run: |
python -m pip install --upgrade pip setuptools_scm[toml] wheel setuptools
python -m pip install --upgrade attrs types-attrs
python -m pip install --upgrade .
python -m pip install flake8 tox mypy
- name: Mypy
run: |
mypy --cache-dir .mypy_cache --install-types --non-interactive ./src
# mypy:
# runs-on: ubuntu-latest
# steps:
# - name: Set up Python ${{ matrix.python-version }}
# uses: actions/setup-python@v4
# with:
# python-version: "3.12"
# - uses: actions/checkout@v3
# - name: Install dependencies
# run: |
# python -m pip install --upgrade pip setuptools_scm[toml] wheel setuptools
# python -m pip install --upgrade attrs types-attrs
# python -m pip install --upgrade .
# python -m pip install flake8 tox mypy
# - name: Mypy
# run: |
# mypy --cache-dir .mypy_cache --install-types --non-interactive ./src

tests:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ["3.9", "3.10", "3.11", "3.12"]
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]

steps:
- uses: actions/checkout@v3
Expand Down
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,9 @@ dist/*
*.egg
/target
/Cargo.lock
.coverage
.coverage
**/target
**/Cargo.lock
**/cov.xml
.hypothesis
dist/
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[![Documentation Status](https://readthedocs.org/projects/qslib/badge/?version=latest)](https://stickydesign.readthedocs.io/en/latest/?badge=latest)
![Codecov](https://img.shields.io/codecov/c/github/DNA-and-Natural-Algorithms-Group/stickydesign)
[![Codecov](https://img.shields.io/codecov/c/github/DNA-and-Natural-Algorithms-Group/stickydesign)](https://codecov.io/gh/DNA-and-Natural-Algorithms-Group/stickydesign)
![GitHub Workflow
Status](https://img.shields.io/github/workflow/status/DNA-and-Natural-Algorithms-Group/stickydesign/Tests)
Status](https://img.shields.io/github/actions/workflow/status/DNA-and-Natural-Algorithms-Group/stickydesign/python-tests.yml?branch=main)
![PyPI](https://img.shields.io/pypi/v/stickydesign)

The StickyDesign Sticky End Sequence Designer
Expand Down
8 changes: 4 additions & 4 deletions accel/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "stickydesign-accel"
version = "0.9.1"
version = "0.9.2"
edition = "2021"

[lib]
Expand All @@ -12,7 +12,7 @@ crate-type = ["cdylib"]
rayon = ["ndarray/rayon"]

[dependencies]
ndarray = {version = "0.15", features = ["approx", "blas", "serde"]}
numpy = {version = "0.21"}
pyo3 = {version = "0.21", features = ["extension-module", "experimental-declarative-modules"]}
ndarray = {version = "0.16", features = ["approx", "blas", "serde"]}
numpy = {version = "0.22"}
pyo3 = {version = "0.22", features = ["extension-module"]}

4 changes: 2 additions & 2 deletions accel/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ build-backend = "maturin"
[project]
name = 'stickydesign-accel'
description = "Accelerated functions in Rust for stickydesign."
dependencies = ['numpy', 'stickydesign ~= 0.9.1']
version = "0.9.1"
dependencies = ['numpy', 'stickydesign ~= 0.9.2']
version = "0.9.2"
authors = [ {name = "Constantine Evans", email = "const@costi.net"} ]
license = {text = "BSD 3-Clause"}
classifiers = [
Expand Down
6 changes: 6 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
Changelog
==========

0.9.2
-----

* Update versions to support Numpy 2.
* Numerous linting and typing fixes.

0.9.1
-----

Expand Down
6 changes: 3 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ testpaths = ["tests"]

[project]
name = "stickydesign"
version = "0.9.1"
version = "0.9.2"
authors = [{ name = "Constantine Evans", email = "const@costi.net" }]
description = "StickyDesign DNA Tile Sticky End Package"
readme = "README.md"
dependencies = ["numpy ~= 1.20", "typing_extensions~=4.0"]
dependencies = ["numpy >= 1.20, < 3.0", "typing_extensions~=4.0"]

[project.urls]
homepage = "https://dna.caltech.edu/StickyDesign"
Expand Down Expand Up @@ -63,7 +63,7 @@ indent-width = 4
fixable = ["ALL"]
select = ["E4", "E7", "E9", "F", "B", "A001", "A002", "C4", "ICN", "PIE", "PYI", "RSE",
"RET501", "SIM", "TID", "TCH", "INT", "PTH", "PD", "PLR", "PLW", "TRY",
"NPY", "PERF", "FURB", "RUF", "UP", "D"]
"NPY", "PERF", "FURB", "RUF", "UP", "D", "NPY201"]

# 2. Avoid enforcing line-length violations (`E501`)
ignore = ["E501", "TRY003", "D1", "UP007", "D205", "D401", "UP032", "UP", "PLR0913", "PLR2004", "PLR0912"]
Expand Down
2 changes: 2 additions & 0 deletions src/stickydesign/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# ruff: noqa: F401, F403

from .version import __version__

from .endclasses import *
Expand Down
16 changes: 9 additions & 7 deletions src/stickydesign/endclasses.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import numpy as np
from typing_extensions import TypeAlias # noqa: UP035
from typing import Union, Literal, List, cast, Dict, Any # noqa: UP035
from collections.abc import Sequence
from abc import ABC, abstractmethod, abstractproperty
from typing_extensions import TypeAlias
from typing import Union, Literal, List, cast, Dict, Any, TYPE_CHECKING
from abc import ABC, abstractmethod

if TYPE_CHECKING:
from collections.abc import Sequence

__all__ = [
'Energetics',
Expand All @@ -17,7 +19,7 @@
'tops',
]

EndTypes: TypeAlias = Literal['DT', 'TD', 'S'] # noqa: UP040
EndTypes: TypeAlias = Literal['DT', 'TD', 'S']


class PairSeqA(np.ndarray):
Expand All @@ -28,7 +30,7 @@ def __new__(cls, array: np.ndarray):
def revcomp(self) -> 'PairSeqA':
return cast(PairSeqA, 4 * (3 - (self[:, ::-1] % 4)) + 3 - (self[:, ::-1] // 4))

def tolist(self) -> List[str]: # noqa: UP006
def tolist(self) -> List[str]:
st = ["a", "c", "g", "t"]
return [
"".join([st[x // 4] for x in y] + [st[y[-1] % 4]]) for y in self
Expand Down Expand Up @@ -159,7 +161,7 @@ def append(s1, s2: 'EndArray') -> 'EndArray':
def __repr__(self):
return f"<endarray ({len(self)}): type {self.endtype}; {self.tolist()!r}>"

def tolist(self) -> List[str]: # noqa: UP006
def tolist(self) -> List[str]:
st = ["a", "c", "g", "t"]
return ["".join([st[x] for x in y]) for y in self]

Expand Down
44 changes: 20 additions & 24 deletions src/stickydesign/energetics_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

def py_tightloop(ens, ltmm, rtmm, intmm, singlepair, looppenalty):
bindmax = np.zeros(ens.shape[0])
for e in range(0, ens.shape[0]):
for e in range(ens.shape[0]):
acc = 0
for i in range(ens.shape[1]):
if ens[e, i] != 0:
Expand All @@ -27,8 +27,7 @@ def py_tightloop(ens, ltmm, rtmm, intmm, singlepair, looppenalty):
# Update: we only want to do this if the last
# nnpair was bound, because otherwise, we
# can't have a "right" mismatch.
if acc + rtmm[e, i] > bindmax[e]:
bindmax[e] = acc + rtmm[e, i]
bindmax[e] = max(acc + rtmm[e, i], bindmax[e])
acc += intmm[e, i]
elif ltmm[e, i] != 0 and i < ens.shape[1] - 1:
# don't do this for the last pair we're mismatching on
Expand All @@ -40,9 +39,7 @@ def py_tightloop(ens, ltmm, rtmm, intmm, singlepair, looppenalty):
# mismatch.
if (not singlepair) and (ltmm[e, i] >
acc + intmm[e, i]) and (
ens[e, i + 1] > 0):
acc = ltmm[e, i]
elif (singlepair) and (ltmm[e, i] >
ens[e, i + 1] > 0) or (singlepair) and (ltmm[e, i] >
acc + intmm[e, i]):
acc = ltmm[e, i]
else:
Expand All @@ -54,22 +51,21 @@ def py_tightloop(ens, ltmm, rtmm, intmm, singlepair, looppenalty):


class EnergeticsBasic(Energetics):

"""Energy functions based on several sources, primarily SantaLucia's
2004 paper. This class uses the same parameters and algorithms
as EnergeticsDAOE, bet does not make DX-specific assumptions.
Instead, it assumes that each energy should simply be that of
two single strands attaching/detaching, without consideration
of nicks, stacking, or other effects related to the
beginning/end of each sequence. Dangles and tails are still
included in mismatched binding calculations when appropriate.
Relevant arguments:
2004 paper. This class uses the same parameters and algorithms
as EnergeticsDAOE, bet does not make DX-specific assumptions.
Instead, it assumes that each energy should simply be that of
two single strands attaching/detaching, without consideration
of nicks, stacking, or other effects related to the
beginning/end of each sequence. Dangles and tails are still
included in mismatched binding calculations when appropriate.
Relevant arguments:
singlepair (bool, default False) --- treat single base pair pairings
as possible.
temperature (float in degrees Celsius, default 37) --- temperature
to use for the model, in C.
singlepair (bool, default False) --- treat single base pair pairings
as possible.
temperature (float in degrees Celsius, default 37) --- temperature
to use for the model, in C.
"""

def __init__(self,
Expand Down Expand Up @@ -98,7 +94,7 @@ def __init__(self,
self._accel = _accel
self._allow_shifts = _allow_shifts
@property
def info(self) -> Dict[str, Any]: # noqa: F821
def info(self) -> Dict[str, Any]:
info = {'enclass': 'EnergeticsDAOE',
'temperature': self.temperature,
'coaxparams': self.coaxparaminfo,
Expand Down Expand Up @@ -158,9 +154,9 @@ def _setup_params(self, temperature=37):
self.intmmdG_5335 = np.zeros(256)

# Dumb setup. FIXME: do this cleverly
for i in range(0, 4):
for j in range(0, 4):
for k in range(0, 4):
for i in range(4):
for j in range(4):
for k in range(4):
self.ltmmdG_5335[
i * 64 + j * 16 + k * 4 +
j] = self.dangle5dG[i * 4
Expand Down
80 changes: 38 additions & 42 deletions src/stickydesign/energetics_daoe.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,9 @@ def _setup_params(self, temperature=37):
self.intmmdG_5335 = np.zeros(256)

# Dumb setup. FIXME: do this cleverly
for i in range(0, 4):
for j in range(0, 4):
for k in range(0, 4):
for i in range(4):
for j in range(4):
for k in range(4):
self.ltmmdG_5335[
i * 64 + j * 16 + k * 4 +
j] = self.dangle5dG[i * 4
Expand Down Expand Up @@ -175,7 +175,7 @@ def uniform(self, seqs1, seqs2, debug=False):
s1.shape[0]) # store for max binding at any offset

if endtype == 'TD':
s1_end = s1[:, 0:-1] #
s1_end = s1[:, 0:-1]
s2_end_rc = s2r[:, 1:]
s1l = np.hstack(((4 * (s2r[:, 0] // 4) + s1[:, 0] // 4).reshape(
-1, 1), s1))
Expand Down Expand Up @@ -254,52 +254,50 @@ def uniform(self, seqs1, seqs2, debug=False):
ltmm = -self.ltmmdG_5335[s1_end[:, :] * 16 + s2_end_rc[:, :]]
rtmm = -self.rtmmdG_5335[s1_end[:, :] * 16 + s2_end_rc[:, :]]
intmm = -self.intmmdG_5335[s1_end[:, :] * 16 + s2_end_rc[:, :]]
else: # offset < 0
if endtype == 'TD':
ens = (s1_end[:, -offset:] == s2_end_rc[:, :offset]) * (
-self.nndG[s1_end[:, -offset:]])
ens[:, 0] += (ens[:, 0] != 0) * (
-self.nndG[s1[:, -1]] - p.tailcordG37 +
self.dangle5dG[s1[:, -1]]) # - for positive sign
ens[:, -1] += (ens[:, -1] != 0) * (
-self.nndG[s2[:, -1]] - p.tailcordG37 +
self.dangle5dG[s2[:, -1]]) # - for positive sign
ltmm = -self.ltmmdG_5335[s1_end[:, -offset:] * 16
+ s2_end_rc[:, :offset]]
rtmm = -self.rtmmdG_5335[s1_end[:, -offset:] * 16
+ s2_end_rc[:, :offset]]
intmm = -self.intmmdG_5335[s1_end[:, -offset:] * 16
+ s2_end_rc[:, :offset]]
elif endtype == 'DT':
ens = (s1_end[:, :offset] == s2_end_rc[:, -offset:]) * (
-self.nndG[s1_end[:, :offset]])
ens[:, 0] += (ens[:, 0] != 0) * (
-self.nndG[s1[:, 0]] - p.tailcordG37 +
self.dangle3dG[s1[:, 0]]) # - for positive sign
ens[:, -1] += (ens[:, -1] != 0) * (
-self.nndG[s2[:, 0]] - p.tailcordG37 +
self.dangle3dG[s2[:, 0]]) # - for positive sign
ltmm = -self.ltmmdG_5335[s1_end[:, :offset] * 16
+ s2_end_rc[:, -offset:]]
rtmm = -self.rtmmdG_5335[s1_end[:, :offset] * 16
+ s2_end_rc[:, -offset:]]
intmm = -self.intmmdG_5335[s1_end[:, :offset] * 16
+ s2_end_rc[:, -offset:]]
elif endtype == 'TD':
ens = (s1_end[:, -offset:] == s2_end_rc[:, :offset]) * (
-self.nndG[s1_end[:, -offset:]])
ens[:, 0] += (ens[:, 0] != 0) * (
-self.nndG[s1[:, -1]] - p.tailcordG37 +
self.dangle5dG[s1[:, -1]]) # - for positive sign
ens[:, -1] += (ens[:, -1] != 0) * (
-self.nndG[s2[:, -1]] - p.tailcordG37 +
self.dangle5dG[s2[:, -1]]) # - for positive sign
ltmm = -self.ltmmdG_5335[s1_end[:, -offset:] * 16
+ s2_end_rc[:, :offset]]
rtmm = -self.rtmmdG_5335[s1_end[:, -offset:] * 16
+ s2_end_rc[:, :offset]]
intmm = -self.intmmdG_5335[s1_end[:, -offset:] * 16
+ s2_end_rc[:, :offset]]
elif endtype == 'DT':
ens = (s1_end[:, :offset] == s2_end_rc[:, -offset:]) * (
-self.nndG[s1_end[:, :offset]])
ens[:, 0] += (ens[:, 0] != 0) * (
-self.nndG[s1[:, 0]] - p.tailcordG37 +
self.dangle3dG[s1[:, 0]]) # - for positive sign
ens[:, -1] += (ens[:, -1] != 0) * (
-self.nndG[s2[:, 0]] - p.tailcordG37 +
self.dangle3dG[s2[:, 0]]) # - for positive sign
ltmm = -self.ltmmdG_5335[s1_end[:, :offset] * 16
+ s2_end_rc[:, -offset:]]
rtmm = -self.rtmmdG_5335[s1_end[:, :offset] * 16
+ s2_end_rc[:, -offset:]]
intmm = -self.intmmdG_5335[s1_end[:, :offset] * 16
+ s2_end_rc[:, -offset:]]
bindmax = np.zeros(ens.shape[0])
if debug:
print(offset, ens.view(np.ndarray), ltmm, rtmm, intmm)
for e in range(0, ens.shape[0]):
for e in range(ens.shape[0]):
acc = 0
for i in range(0, ens.shape[1]):
for i in range(ens.shape[1]):
if ens[e, i] != 0:
# we're matching. add the pair to the accumulator
acc += ens[e, i]
elif rtmm[e, i] != 0 and i > 0 and ens[e, i-1] > 0:
# we're mismatching on the right: see if right-dangling
# is highest binding so far, and continue, adding intmm
# to accumulator.
if acc + rtmm[e, i] > bindmax[e]:
bindmax[e] = acc + rtmm[e, i]
bindmax[e] = max(acc + rtmm[e, i], bindmax[e])
acc += intmm[e, i]
elif ltmm[e, i] != 0 and i < ens.shape[1] - 1:
# don't do this for the last pair we're mismatching on
Expand All @@ -311,9 +309,7 @@ def uniform(self, seqs1, seqs2, debug=False):
# mismatch.
if (not self.singlepair) and (ltmm[e, i] >
acc + intmm[e, i]) and (
ens[e, i + 1] > 0):
acc = ltmm[e, i]
elif (self.singlepair) and (ltmm[e, i] >
ens[e, i + 1] > 0) or (self.singlepair) and (ltmm[e, i] >
acc + intmm[e, i]):
acc = ltmm[e, i]
else:
Expand Down
Loading

0 comments on commit 37ff57d

Please sign in to comment.