Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python

name: CI

on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]


jobs:
linting:
name: Linting
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Linting
run: |
pip install pre-commit
pre-commit run --all-files

build:
name: build
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.9", "3.10", "3.11", "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: build
run: pip install .
- name: test import
run: python -c "import pysensors"

tests:
name: Tests
needs: linting
runs-on: ubuntu-latest
strategy:
fail-fast: false
max-parallel: 4
matrix:
python-version: ["3.9", "3.10", "3.11", "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 dependencies
run: |
pip install -r requirements-dev.txt
- name: Test with pytest
run: |
pytest tests --cov=pysensors --cov-report=xml
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
file: ./coverage.xml
- name: Execute feature notebook with papermill
run: |
pip install papermill
cd examples
papermill --report-mode pysensors_overview.ipynb out.json
- uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements-dev.txt') }}
restore-keys: |
${{ runner.os }}-pip-
54 changes: 0 additions & 54 deletions .github/workflows/run-tests.yml

This file was deleted.

1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ coverage.xml

# virtual environment
venv/*
env/*

.ipynb_checkpoints
*/.ipynb_checkpoints/*
Expand Down
22 changes: 13 additions & 9 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,22 +1,26 @@

fail_fast: false
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.3.0
rev: v5.0.0
hooks:
- id: check-added-large-files
args: ["--maxkb=775"]
- id: check-merge-conflict
- repo: https://github.com/asottile/reorder_python_imports
rev: v3.8.2
- repo: https://github.com/PyCQA/isort
rev: 5.12.0
hooks:
- id: reorder-python-imports
- repo: https://github.com/ambv/black
rev: 22.8.0
- id: isort
args: [--profile=black]
- repo: https://github.com/psf/black
rev: 25.1.0
hooks:
- id: black
- repo: https://gitlab.com/pycqa/flake8
rev: 3.9.2
- repo: https://github.com/sphinx-contrib/sphinx-lint
rev: v0.6.1
hooks:
- id: sphinx-lint
- repo: https://github.com/pycqa/flake8
rev: 7.1.1
hooks:
- id: flake8
args: ["--config=setup.cfg"]
6 changes: 3 additions & 3 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@ PySensors provides the ``SSPOR`` (Sparse Sensor Placement Optimization for Recon
Take representative examples of the types of data to be reconstructed (in this case polynomials)

.. code-block:: python

x = numpy.linspace(0, 1, 1001)
data = numpy.vander(x, 11).T # Create an array whose rows are powers of x

feed them to a ``SSPOR`` instance with 10 sensors, and
feed them to a ``SSPOR`` instance with 10 sensors, and

.. code-block:: python

Expand Down Expand Up @@ -265,7 +265,7 @@ References

.. |JOSS| image:: https://joss.theoj.org/papers/10.21105/joss.02828/status.svg
:target: https://doi.org/10.21105/joss.02828

.. |Zenodo| image:: https://zenodo.org/badge/260577702.svg
:target: https://zenodo.org/badge/latestdoi/260577702

2 changes: 1 addition & 1 deletion docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@


PySensors @ PyPI <https://pypi.org/project/python-sensors/>
Issue Tracker <https://github.com/dynamicslab/pysensors/issues>
Issue Tracker <https://github.com/dynamicslab/pysensors/issues>
100 changes: 42 additions & 58 deletions examples/classification.ipynb

Large diffs are not rendered by default.

30 changes: 12 additions & 18 deletions examples/vandermonde.ipynb

Large diffs are not rendered by default.

8 changes: 3 additions & 5 deletions pysensors/__init__.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
from pkg_resources import DistributionNotFound
from pkg_resources import get_distribution
from importlib.metadata import PackageNotFoundError, distribution

try:
__version__ = get_distribution("python-sensors").version
except DistributionNotFound:
__version__ = distribution("python-sensors").version
except PackageNotFoundError:
pass

from .classification import SSPOC
from .reconstruction import SSPOR


__all__ = [
# Modules:
"basis",
Expand Down
4 changes: 2 additions & 2 deletions pysensors/basis/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from ._custom import Custom
from ._identity import Identity
from ._random_projection import RandomProjection
from ._svd import SVD
from ._custom import Custom

__all__ = ["Identity", "SVD", "RandomProjection","Custom"]
__all__ = ["Identity", "SVD", "RandomProjection", "Custom"]
3 changes: 1 addition & 2 deletions pysensors/basis/_base.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from abc import ABC
from abc import abstractmethod
from abc import ABC, abstractmethod

from sklearn.utils.validation import check_is_fitted

Expand Down
36 changes: 23 additions & 13 deletions pysensors/basis/_custom.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
"""
custom mode basis class.
"""
from ._base import InvertibleBasis
from ._base import MatrixMixin

from ._base import InvertibleBasis, MatrixMixin


class Custom(InvertibleBasis, MatrixMixin):
"""
Expand All @@ -27,11 +28,11 @@ class Custom(InvertibleBasis, MatrixMixin):
"""

def __init__(self, U, n_basis_modes=10, **kwargs):
'''
kwargs : Not defined but added to remain consistent with prior basis functions.
'''
"""
kwargs : Not defined but added to remain consistent with prior basis functions.
"""
if isinstance(n_basis_modes, int) and n_basis_modes > 0:
super(Custom, self).__init__()#n_components=n_basis_modes, **kwargs
super(Custom, self).__init__() # n_components=n_basis_modes, **kwargs
self._n_basis_modes = n_basis_modes
self.custom_basis_ = U
else:
Expand All @@ -48,13 +49,22 @@ def fit(self, X):
-------
self : instance
"""
# self.basis_matrix_ = self.custom_basis_[:,: self.n_basis_modes] @ self.custom_basis_[:,: self.n_basis_modes].T @ X[: self.n_basis_modes, :].T.copy()
# self.basis_matrix_ = self.custom_basis_ @ self.custom_basis_.T @ X[: self.n_basis_modes, :].T.copy()
self.basis_matrix_ = self.custom_basis_[:,:self.n_basis_modes]
# self.basis_matrix_ = (X @ self.custom_basis_[:,:self.n_basis_modes] @ self.custom_basis_[:,:self.n_basis_modes].T)[:self.n_basis_modes,:].T

# self.basis_matrix_ = ((X @ self.custom_basis_).T)[:,:self.n_basis_modes]
# self.basis_matrix_ = ((X @ self.custom_basis_ @ self.custom_basis_.T).T)[:,:self.n_basis_modes]
# self.basis_matrix_ = self.custom_basis_[
# :,: self.n_basis_modes] @ self.custom_basis_[
# :,: self.n_basis_modes].T @ X[
# : self.n_basis_modes, :].T.copy()
# self.basis_matrix_ = self.custom_basis_ @ self.custom_basis_.T @ X[
# : self.n_basis_modes, :].T.copy()
self.basis_matrix_ = self.custom_basis_[:, : self.n_basis_modes]
# self.basis_matrix_ = (X @ self.custom_basis_[
# :,:self.n_basis_modes] @ self.custom_basis_[
# :,:self.n_basis_modes].T)[:self.n_basis_modes,:].T

# self.basis_matrix_ = ((X @ self.custom_basis_).T)[
# :,:self.n_basis_modes]
# self.basis_matrix_ = ((
# X @ self.custom_basis_ @ self.custom_basis_.T).T)[
# :,:self.n_basis_modes]
return self

def matrix_inverse(self, n_basis_modes=None):
Expand Down
4 changes: 2 additions & 2 deletions pysensors/basis/_identity.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@

This is essentially a dummy basis which just uses raw, unaltered features.
"""

from warnings import warn

from numpy import identity
from sklearn.base import BaseEstimator
from sklearn.utils import check_array

from ._base import InvertibleBasis
from ._base import MatrixMixin
from ._base import InvertibleBasis, MatrixMixin


class Identity(BaseEstimator, InvertibleBasis, MatrixMixin):
Expand Down
4 changes: 2 additions & 2 deletions pysensors/basis/_random_projection.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@

Project data onto random features.
"""

from numpy.linalg import pinv
from sklearn.random_projection import GaussianRandomProjection

from ._base import InvertibleBasis
from ._base import MatrixMixin
from ._base import InvertibleBasis, MatrixMixin


class RandomProjection(GaussianRandomProjection, InvertibleBasis, MatrixMixin):
Expand Down
4 changes: 2 additions & 2 deletions pysensors/basis/_svd.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
"""
SVD mode basis class.
"""

from sklearn.decomposition import TruncatedSVD

from ._base import InvertibleBasis
from ._base import MatrixMixin
from ._base import InvertibleBasis, MatrixMixin


class SVD(TruncatedSVD, InvertibleBasis, MatrixMixin):
Expand Down
12 changes: 7 additions & 5 deletions pysensors/classification/_sspoc.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
Proceedings of the National Academy of Sciences
115.42 (2018): 10564-10569.
"""

import warnings

import numpy as np
Expand All @@ -24,10 +25,11 @@
from sklearn.utils.validation import check_is_fitted

from ..basis import Identity
from ..utils import constrained_binary_solve
from ..utils import constrained_multiclass_solve
from ..utils import validate_input

from ..utils import (
constrained_binary_solve,
constrained_multiclass_solve,
validate_input,
)

INT_DTYPES = (int, np.int64, np.int32, np.int16, np.int8)

Expand Down Expand Up @@ -259,7 +261,7 @@ def fit(

if self.threshold is None:
# Chosen as in Brunton et al. (2016)
threshold = np.sqrt(np.sum(s ** 2)) / (
threshold = np.sqrt(np.sum(s**2)) / (
2 * self.basis_matrix_inverse_.shape[0] * n_classes
)
else:
Expand Down
8 changes: 2 additions & 6 deletions pysensors/optimizers/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
from ._ccqr import CCQR
from ._qr import QR
from ._gqr import GQR
from ._qr import QR

__all__ = [
"CCQR",
"QR",
"GQR"
]
__all__ = ["CCQR", "QR", "GQR"]
Loading
Loading