diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000..d9127d6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,11 @@ +### Description + +Describe what you were trying to get done. +Tell us what happened, what went wrong, and what you expected to happen. + +### What I Did + +``` +Paste the command(s) you ran and the output. +If there was a crash, please include the traceback here. +``` diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml new file mode 100644 index 0000000..2642956 --- /dev/null +++ b/.github/workflows/build_and_test.yml @@ -0,0 +1,45 @@ +name: Build and Test + +on: + push: + branches: [ master, develop ] + pull_request: + branches: [ master, develop ] + +jobs: + build_linux: + name: "Build and test" + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + 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: Upgrade pip. setuptools and wheel + run: python -m pip install --upgrade pip setuptools wheel + + - name: Install development dependencies + run: pip install flake8 pytest pytest-cov + - name: Lint with flake8 + run: | + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: Install package as editable + run: pip install -e . + - name: Test with pytest + run: | + pytest tests --junitxml=junit/test-results-${{ matrix.os }}-${{ matrix.python-version }}.xml --cov=com --cov-report=xml --cov-report=html + - name: Upload pytest test results + uses: actions/upload-artifact@v4 + with: + name: pytest-results-${{ matrix.os }}-${{ matrix.python-version }} + path: junit/test-results-${{ matrix.os }}-${{ matrix.python-version }}.xml + # Use always() to always run this step to publish test results when there are test failures + if: ${{ always() }} diff --git a/.github/workflows/pypi-publish.yml b/.github/workflows/pypi-publish.yml new file mode 100644 index 0000000..ce25ff7 --- /dev/null +++ b/.github/workflows/pypi-publish.yml @@ -0,0 +1,31 @@ +# This workflows will upload a Python Package using Twine when a release is created +# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries + +name: Upload Python Package + +on: + release: + types: [created] + +jobs: + deploy: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.x' + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install setuptools wheel twine + - name: Build and publish + env: + TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} + TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} + run: | + python setup.py sdist bdist_wheel + twine upload dist/* diff --git a/.gitignore b/.gitignore index 6c92abd..49b36ee 100644 --- a/.gitignore +++ b/.gitignore @@ -117,3 +117,6 @@ docs/_build/ # PyBuilder target/ +# Environments +.conda/ +.vscode/ diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 1ab86a6..0000000 --- a/.travis.yml +++ /dev/null @@ -1,35 +0,0 @@ -language: python -sudo: false -python: - - 2.6 - - 2.7 - - 3.3 - - 3.4 - - 3.5 - - "3.5-dev" - - "nightly" - - pypy -matrix: - allow_failures: - - python: 2.6 - - python: "3.5-dev" - - python: "nightly" - - python: pypy -branches: - only: - - master - - develop -addons: - apt: - packages: - - libblas-dev - - liblapack-dev - - libatlas-dev - - gfortran - - build-essential -install: - - "pip install pytest pytest-cov python-coveralls" - - "travis_wait pip install -r requirements.txt" -script: py.test tests/ --cov lspopt --cov-report term-missing -after_success: - - coveralls diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..6fb5bb1 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,79 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [1.4.0] - 2025-01-07 + +### Added + +- Support for Python 3.12 +- Support for `pip>=25` +- Support for `numpy>=2` +- Support for Windows and macOS + +### Removed + +- Support for Python 3.7 and 3.8 +- Unused imports + + +## [1.3.0] - 2023-01-24 + +### Changed + +- Modified test matrix in CI + +### Removed + +- Support for Python 2.7 and 3.6. +- Dependency on `six`. + + +## [1.2.0] - 2022-06-08 + +### Added + +- New plot file + +### Fixed + +- Source distribution was broken on PyPI. Modified `MANIFEST.in` to correct that (#5 and #6) +- Url to missing plot file +- Fixed some incorrect int declarations using `1e3` notation + +### Removed + +- Removed Pipfile + +## [1.1.1] - 2020-09-28 + +### Added + +- Added `CHANGELOG.md` + +### Changed + +- Change CI from Azure Devops to Github Actions + + +## [1.1.0] - 2019-06-19 + +### Added + +- First PyPI-released version + + +## [1.0.0] - 2016-08-22 + +### Added + +- Regarded as a feature-complete, stable library. + + +[Unreleased]: https://github.com/hbldh/lspopt/compare/v1.3.0...HEAD +[1.3.0]: https://github.com/hbldh/lspopt/compare/v1.2.0...v1.3.0 +[1.2.0]: https://github.com/hbldh/lspopt/compare/v1.1.1...v1.2.0 +[1.1.1]: https://github.com/hbldh/lspopt/compare/v1.1.0...v1.1.1 +[1.1.0]: https://github.com/hbldh/lspopt/compare/v1.0.0...v1.1.0 diff --git a/LICENSE b/LICENSE index 8295bc7..03e60a3 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2015 Henrik Blidh +Copyright (c) 2022 Henrik Blidh Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..63be52a --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,8 @@ +include LICENSE +include README.md +include HISTORY.md +include requirements.txt +include Pipfile +include CHANGELOG.md +include lspopt/data/c.npy +include lspopt/data/weights.npy diff --git a/README.md b/README.md index 562b0e6..7ca5995 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ # LSPOpt -[![Build Status](https://travis-ci.org/hbldh/lspopt.svg)](https://travis-ci.org/hbldh/lspopt) -[![Coverage Status](https://coveralls.io/repos/github/hbldh/lspopt/badge.svg?branch=master)](https://coveralls.io/github/hbldh/lspopt?branch=master) +![Build and Test](https://github.com/hbldh/lspopt/workflows/Build%20and%20Test/badge.svg) +[![PyPI version](https://img.shields.io/pypi/v/lspopt.svg)](https://pypi.org/project/lspopt/) +[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black) This module is a Python implementation of the multitaper window method described in [\[1\]](#references) for estimating Wigner spectra for certain locally @@ -22,16 +23,17 @@ Abstract from [\[1\]](#references): Install via pip: - pip install git+https://github.com/hbldh/lspopt.git#egg=lspopt + pip install lspopt + +If you prefer to use `conda`, see [instructions in this repo](https://github.com/conda-forge/lspopt-feedstock). ## Testing -Test with `nosetests`: +Test with `pytest`: - nosetests tests/ + pytest tests/ -Tests are run at every commit to GitHub and the results of this, as well as test -coverage, can be studied at [Travis CI](https://travis-ci.org/hbldh/lspopt). +See test badge at the top of this README for link to test coverage and reports. ## Usage @@ -39,7 +41,7 @@ To generate the taper windows only, use the `lspopt` method: ```python from lspopt import lspopt -H, w = lspopt(N=256, c_parameter=20.0) +H, w = lspopt(n=256, c_parameter=20.0) ``` There is also a convenience method for using the [SciPy spectrogram method](https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.spectrogram.html#scipy.signal.spectrogram) @@ -64,13 +66,13 @@ import matplotlib.pyplot as plt from lspopt.lsp import spectrogram_lspopt -fs = 10e3 -N = 1e5 +fs = 10000 +N = 100000 amp = 2 * np.sqrt(2) noise_power = 0.001 * fs / 2 time = np.arange(N) / fs -freq = np.linspace(1e3, 2e3, N) -x = amp * chirp(time, 1e3, 2.0, 6e3, method='quadratic') + \ +freq = np.linspace(1000, 2000, N) +x = amp * chirp(time, 1000, 2.0, 6000, method='quadratic') + \ np.random.normal(scale=np.sqrt(noise_power), size=time.shape) f, t, Sxx = spectrogram(x, fs) @@ -87,14 +89,16 @@ ax.pcolormesh(t, f, Sxx) ax.set_ylabel('Frequency [Hz]') ax.set_xlabel('Time [sec]') +plt.tight_layout() plt.show() ``` -![Spectrogram plot](https://hbldh.github.com/lspopt/images/plot.png) +![Spectrogram plot](https://github.com/hbldh/lspopt/blob/master/plot.png "Top: Using SciPy's spectrogram method. Bottom: Using LSPOpt's spectrogram solution.") + *Top: Using SciPy's spectrogram method. Bottom: Using LSPOpt's spectrogram solution.* ## References \[1\] [Hansson-Sandsten, M. (2011). Optimal multitaper Wigner spectrum estimation of a class of locally stationary processes using Hermite functions. -EURASIP Journal on Advances in Signal Processing, 2011, 10.](http://asp.eurasipjournals.com/content/pdf/1687-6180-2011-980805.pdf) +EURASIP Journal on Advances in Signal Processing, 2011, 10.](https://dx.doi.org/10.1155/2011/980805) diff --git a/generate_plot.py b/generate_plot.py index d7ea04e..37197f0 100644 --- a/generate_plot.py +++ b/generate_plot.py @@ -9,11 +9,6 @@ """ -from __future__ import division -from __future__ import print_function -from __future__ import unicode_literals -from __future__ import absolute_import - import six import numpy as np from scipy.signal import chirp, spectrogram @@ -26,13 +21,13 @@ def generate_chirp_spectrogram_plot(): - fs = 10e3 - N = 1e5 + fs = 10000 + N = 100000 amp = 2 * np.sqrt(2) noise_power = 0.001 * fs / 2 time = np.arange(N) / fs - freq = np.linspace(1e3, 2e3, N) - x = amp * chirp(time, 1e3, 2.0, 6e3, method='quadratic') + \ + freq = np.linspace(1000, 2000, N) + x = amp * chirp(time, 1000, 2.0, 6000, method='quadratic') + \ np.random.normal(scale=np.sqrt(noise_power), size=time.shape) f, t, Sxx = spectrogram(x, fs) @@ -40,7 +35,7 @@ def generate_chirp_spectrogram_plot(): ax = plt.subplot(211) ax.pcolormesh(t, f, Sxx) ax.set_ylabel('Frequency [Hz]') - ax.set_xlabel('Time [sec]') + ax.set_xlabel('Time [sec]', labelpad=10) f, t, Sxx = spectrogram_lspopt(x, fs, c_parameter=20.0) @@ -49,6 +44,7 @@ def generate_chirp_spectrogram_plot(): ax.set_ylabel('Frequency [Hz]') ax.set_xlabel('Time [sec]') + plt.tight_layout() plt.show() @@ -100,11 +96,12 @@ def generate_lsp_processes_figures(): ax.set_ylim([-1.5, 1.5]) ax.set_xlabel('t') + plt.tight_layout() plt.show() def main(): - #generate_chirp_spectrogram_plot() + generate_chirp_spectrogram_plot() generate_lsp_processes_figures() if __name__ == "__main__": diff --git a/lspopt/__init__.py b/lspopt/__init__.py index 7b4a19d..e507e97 100644 --- a/lspopt/__init__.py +++ b/lspopt/__init__.py @@ -1,69 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -from __future__ import division -from __future__ import print_function -from __future__ import unicode_literals -from __future__ import absolute_import +from lspopt.lsp import lspopt, spectrogram_lspopt +from lspopt.__version__ import __version__, version -try: - from lspopt.lsp import lspopt, spectrogram_lspopt - from tests.lspopt_ref import lspopt_ref - from lspopt import utils - - __all__ = ['lspopt', 'spectrogram_lspopt', 'utils'] -except ImportError: - # We will end up here in installation case. - pass - -# Version information. An empty _version_extra corresponds to a full -# release. 'dev' as a _version_extra string means this is a development -# version. -_version_major = 1 -_version_minor = 0 -_version_patch = 0 -# _version_extra = 'dev1' -# _version_extra = 'a6' -_version_extra = '' # Uncomment this for full releases - -# Construct full version string from these. -_ver = [_version_major, _version_minor, _version_patch] - -__version__ = '.'.join(map(str, _ver)) -if _version_extra: - __version__ += _version_extra - -version = __version__ # backwards compatibility name -version_info = (_version_major, _version_minor, _version_patch, _version_extra) - -__name__ = 'lspopt' -__author__ = 'Henrik Blidh' -__author_email__ = 'henrik.blidh@nedomkull.com' -__maintainer__ = 'Henrik Blidh' -__maintainer_email__ = 'henrik.blidh@nedomkull.com' -__license__ = 'MIT' -__description__ = "A Python implementation of a multitaper window method for " \ - "estimating Wigner spectra for certain locally stationary processes" -__url__ = 'https://github.com/hbldh/lspopt' -__download_url__ = 'https://github.com/hbldh/lspopt/tarball/' + '.'.join(map(str, _ver)) -__platforms__ = ['Linux', 'Mac OSX', 'Windows XP/Vista/7/8'] -__keywords__ = ['Mathmatical Statistics', 'Multitaper', 'Spectrogram'] -__classifiers__ = [ - 'Development Status :: 5 - Production/Stable', - 'Intended Audience :: Science/Research', - 'Intended Audience :: Developers', - 'License :: MIT', - 'Topic :: Scientific/Engineering', - 'Operating System :: Microsoft :: Windows', - 'Operating System :: POSIX', - 'Operating System :: Unix', - 'Operating System :: MacOS', - 'Programming Language :: Python', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.6', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.3', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', - ], +__all__ = ["lspopt", "spectrogram_lspopt"] diff --git a/lspopt/__version__.py b/lspopt/__version__.py new file mode 100644 index 0000000..5add2d0 --- /dev/null +++ b/lspopt/__version__.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +version = "1.4.0" +__version__ = version diff --git a/lspopt/data/__init__.py b/lspopt/data/__init__.py index 9d47c10..5726c28 100644 --- a/lspopt/data/__init__.py +++ b/lspopt/data/__init__.py @@ -8,22 +8,17 @@ """ -from __future__ import division -from __future__ import print_function -from __future__ import unicode_literals -from __future__ import absolute_import - -from pkg_resources import resource_filename +from importlib import resources import numpy as np -__all__ = ['C', 'WEIGHTS', 'f_h'] +__all__ = ["C", "WEIGHTS", "f_h"] # An array of C parameter values for which weights have been pre-calculated. -C = np.load(resource_filename('lspopt.data', 'c.npy')).flatten() +C = np.load(resources.files("lspopt.data").joinpath("c.npy")).flatten() # The pre-calculated Hermite polynomial coefficients # for the C parameter values above. -WEIGHTS = np.load(resource_filename('lspopt.data', 'weights.npy')) +WEIGHTS = np.load(resources.files("lspopt.data").joinpath("weights.npy")) def f_h(n, k): @@ -39,6 +34,7 @@ def f_h(n, k): """ return n / _K_TO_VALUE_.get(k) + # Given length of Hermite polynomial coefficient array, return # a value to divide N with. _K_TO_VALUE_ = { @@ -51,5 +47,5 @@ def f_h(n, k): 7: 9.8, 8: 10.3, 9: 10.9, - 10: 11.2 + 10: 11.2, } diff --git a/lspopt/lsp.py b/lspopt/lsp.py index 49f6f44..8a24abc 100644 --- a/lspopt/lsp.py +++ b/lspopt/lsp.py @@ -14,12 +14,6 @@ """ -from __future__ import division -from __future__ import print_function -from __future__ import unicode_literals -from __future__ import absolute_import - -import six import numpy as np from scipy.signal import spectrogram @@ -60,19 +54,27 @@ def lspopt(n, c_parameter=20.0): t1 = np.arange(-(n / 2) + 1, (n / 2) + 0.1, step=1.0) / f_h(n, K) h = np.vstack((np.ones((n,)), 2 * t1)) - for i in six.moves.range(1, K - 1): + for i in range(1, K - 1): h = np.vstack((h, (2 * t1 * h.T[:, i]) - 2 * i * h.T[:, i - 1])) - H = h.T * np.outer(np.exp(-(t1**2)/2), np.ones((K,), 'float')) + H = h.T * np.outer(np.exp(-(t1 ** 2) / 2), np.ones((K,), "float")) H /= np.array([np.linalg.norm(x) for x in H.T]) return H.T.copy(), weights -def spectrogram_lspopt(x, fs=1.0, c_parameter=20.0, - nperseg=256, noverlap=None, nfft=None, - detrend='constant', return_onesided=True, - scaling='density', axis=-1): +def spectrogram_lspopt( + x, + fs=1.0, + c_parameter=20.0, + nperseg=256, + noverlap=None, + nfft=None, + detrend="constant", + return_onesided=True, + scaling="density", + axis=-1, +): """Convenience method for calling :py:meth:`scipy.signal.spectrogram` with :py:mod:`lspopt` multitaper windows. @@ -144,15 +146,20 @@ def spectrogram_lspopt(x, fs=1.0, c_parameter=20.0, S_out = None # Call spectrogram method for each taper window. for taper_window, taper_weight in zip(H, taper_weights): - f, t, Pxx = spectrogram(x, fs=fs, window=taper_window, - nperseg=nperseg, noverlap=noverlap, - nfft=nfft, detrend=detrend, - return_onesided=return_onesided, - scaling=scaling, axis=axis) + f, t, Pxx = spectrogram( + x, + fs=fs, + window=taper_window, + nperseg=nperseg, + noverlap=noverlap, + nfft=nfft, + detrend=detrend, + return_onesided=return_onesided, + scaling=scaling, + axis=axis, + ) if S_out is None: S_out = taper_weight * Pxx else: S_out += taper_weight * Pxx return f, t, S_out - - diff --git a/lspopt/utils.py b/lspopt/utils.py index 21a52d8..03cd37e 100644 --- a/lspopt/utils.py +++ b/lspopt/utils.py @@ -14,11 +14,6 @@ """ -from __future__ import division -from __future__ import print_function -from __future__ import unicode_literals -from __future__ import absolute_import - import numpy as np from scipy.linalg import sqrtm @@ -93,7 +88,11 @@ def create_lscp_realisation(N, c, Fs, m, d, random_seed=None): EURASIP Journal on Advances in Signal Processing, 2011, 10. """ - r_x = lambda a, b: _q((a + b) / 2) * _r(a - b, c) * np.exp(1j * m * (a - b) * ((a + b) / 2 - d)) + r_x = ( + lambda a, b: _q((a + b) / 2) + * _r(a - b, c) + * np.exp(1j * m * (a - b) * ((a + b) / 2 - d)) + ) return _create_realisation(r_x, N, Fs, random_seed) @@ -135,13 +134,13 @@ def create_mlsp_realisation(N, c_vector, Fs, random_seed=None): def _create_realisation(r_x, N, Fs, random_seed): # Sampling vector. - t_vector = np.arange(-(N/2), N/2, 1) / Fs + t_vector = np.arange(-(N / 2), N / 2, 1) / Fs # Create real or complex matrix depending on covariance function output. if np.iscomplex(r_x(t_vector[0], t_vector[-1])): - Rx = np.zeros((N, N), 'complex') + Rx = np.zeros((N, N), "complex") else: - Rx = np.zeros((N, N), 'float') + Rx = np.zeros((N, N), "float") # Calculate covariance matrix. for i, t in enumerate(t_vector): @@ -170,8 +169,8 @@ def _create_realisation(r_x, N, Fs, random_seed): def _q(tau): - return np.exp(-(tau ** 2)/2) + return np.exp(-(tau ** 2) / 2) def _r(tau, c): - return np.exp(-((c/4) * (tau ** 2)) / 2) + return np.exp(-((c / 4) * (tau ** 2)) / 2) diff --git a/plot.png b/plot.png new file mode 100644 index 0000000..b996901 Binary files /dev/null and b/plot.png differ diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..b3d5b86 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["setuptools >= 64", "wheel"] +build-backend = "setuptools.build_meta" diff --git a/requirements.txt b/requirements.txt index fbbeba1..aae5d11 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,2 @@ -numpy>=1.6.2 -scipy>=0.16.1 -six>=1.10.0 +numpy>=1.21.6 +scipy>=1.7.3 diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..d662cb9 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,5 @@ +[bdist_wheel] +universal=1 + +[aliases] +test=pytest diff --git a/setup.py b/setup.py index d078c18..9a08fda 100644 --- a/setup.py +++ b/setup.py @@ -1,52 +1,124 @@ +#!/usr/bin/env python # -*- coding: utf-8 -*- -""" -Release data for the lspopt project. +# Note: To use the 'upload' functionality of this file, you must: +# $ pip install twine -Copyright (c) 2015, Nedomkull Mathematical Modeling AB. -Created on 2015-11-15 by hbldh +import io +import os +import sys +from shutil import rmtree -""" +from setuptools import find_packages, setup, Command -from __future__ import division -from __future__ import print_function -from __future__ import absolute_import +# Package meta-data. +NAME = 'lspopt' +DESCRIPTION = "A Python implementation of a multitaper window method for " \ + "estimating Wigner spectra for certain locally stationary processes" +URL = 'https://github.com/hbldh/lspopt' +EMAIL = 'henrik.blidh@gmail.com' +AUTHOR = 'Henrik Blidh' +REQUIRES_PYTHON = '>=3.9' +VERSION = None -import os -from setuptools import setup, find_packages +REQUIRED = [ + 'numpy>=1.21.6', + 'scipy>=1.7.3', +] + +here = os.path.abspath(os.path.dirname(__file__)) + +with io.open(os.path.join(here, 'README.md'), encoding='utf-8') as f: + long_description = '\n' + f.read() + +with io.open(os.path.join(here, 'CHANGELOG.md'), encoding='utf-8') as f: + long_description = long_description + '\n' + f.read() + +# Load the package's __version__.py module as a dictionary. +about = {} +if not VERSION: + with open(os.path.join(here, NAME, '__version__.py')) as f: + exec(f.read(), about) +else: + about['__version__'] = VERSION + + +class UploadCommand(Command): + """Support setup.py upload.""" + + description = 'Build and publish the package.' + user_options = [] -import lspopt + @staticmethod + def status(s): + """Prints things in bold.""" + print('\033[1m{0}\033[0m'.format(s)) + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + try: + self.status('Removing previous builds…') + rmtree(os.path.join(here, 'dist')) + except OSError: + pass + + self.status('Building Source and Wheel (universal) distribution…') + os.system('{0} setup.py sdist bdist_wheel --universal'.format(sys.executable)) + + self.status('Uploading the package to PyPi via Twine…') + os.system('twine upload dist/*') + + self.status('Pushing git tags…') + os.system('git tag v{0}'.format(about['__version__'])) + os.system('git push --tags') + + sys.exit() -basedir = os.path.dirname(os.path.abspath(__file__)) -with open(os.path.join(basedir, 'README.md')) as f: - LONG_DESCRIPTION = f.read() setup( - name='lspopt', - version=lspopt.__version__, - author=lspopt.__author__, - author_email=lspopt.__author_email__, - maintainer=lspopt.__maintainer__, - maintainer_email=lspopt.__maintainer_email__, - url=lspopt.__url__, - download_url=lspopt.__download_url__, - description=lspopt.__description__, - long_description=LONG_DESCRIPTION, - license=lspopt.__license__, - platforms=lspopt.__platforms__, - keywords=lspopt.__keywords__, - classifiers=lspopt.__classifiers__, - packages=find_packages(exclude=('tests', )), + name=NAME, + version=about['__version__'], + description=DESCRIPTION, + long_description=long_description, + long_description_content_type='text/markdown', + author=AUTHOR, + author_email=EMAIL, + python_requires=REQUIRES_PYTHON, + tests_require=['pytest', ], + url=URL, + keywords=['Mathematical Statistics', 'Multitaper', 'Spectrogram'], + packages=find_packages(exclude=('tests',)), package_data={ 'lspopt.data': ['*.npy'] }, - install_requires=[ - 'numpy>=1.6.2', - 'scipy>=0.16.1', - 'six>=1.10.0' + install_requires=REQUIRED, + include_package_data=True, + license='MIT', + classifiers=[ + 'Development Status :: 5 - Production/Stable', + 'Intended Audience :: Science/Research', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: MIT License', + 'Topic :: Scientific/Engineering', + 'Operating System :: Microsoft :: Windows', + 'Operating System :: POSIX', + 'Operating System :: Unix', + 'Operating System :: MacOS', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.11', + 'Programming Language :: Python :: 3.12', + 'Programming Language :: Python :: Implementation :: CPython', + 'Programming Language :: Python :: Implementation :: PyPy' ], - dependency_links=[], - ext_modules=[], - entry_points={} + # $ setup.py publish support. + cmdclass={ + 'upload': UploadCommand, + }, ) - diff --git a/tests/__init__.py b/tests/__init__.py index 3b236f4..89072e0 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1 +1 @@ -__author__ = 'hbldh' +__author__ = "hbldh" diff --git a/tests/lspopt_ref.py b/tests/lspopt_ref.py index c59e8c9..5c2ab02 100644 --- a/tests/lspopt_ref.py +++ b/tests/lspopt_ref.py @@ -14,12 +14,6 @@ """ -from __future__ import division -from __future__ import print_function -from __future__ import unicode_literals -from __future__ import absolute_import - -import six import numpy as np from lspopt.data import C, WEIGHTS @@ -71,9 +65,9 @@ def lspopt_ref(n, c_parameter=20.0): if K > 2: for i in range(1, K - 1): h = np.vstack((h, (2 * t1 * h.T[:, i]) - 2 * i * h.T[:, i - 1])) - H = h.T * np.outer(np.exp(-(t1**2)/2), np.ones((K,), 'float')) + H = h.T * np.outer(np.exp(-(t1 ** 2) / 2), np.ones((K,), "float")) - for i in six.moves.range(K): + for i in range(K): H[:, i] = H[:, i] / np.sqrt(H[:, i].T.dot(H[:, i])) # Norm return H.T, wei diff --git a/tests/test_implementation.py b/tests/test_implementation.py index 93348bc..81550b9 100644 --- a/tests/test_implementation.py +++ b/tests/test_implementation.py @@ -14,15 +14,7 @@ """ -from __future__ import division -from __future__ import print_function -from __future__ import unicode_literals -from __future__ import absolute_import - -import unittest - import pytest -import six import numpy as np from numpy.testing import assert_allclose from scipy.signal import chirp, spectrogram @@ -31,7 +23,7 @@ from .lspopt_ref import lspopt_ref -@pytest.mark.parametrize("n", six.moves.range(64, 1024)) +@pytest.mark.parametrize("n", range(64, 1024)) def test_different_n(n): """Test against reference implementation for different N.""" h1, w1 = lspopt(n) @@ -51,14 +43,15 @@ def test_different_c(c): def test_spectrogram_method(): """Test the spectrogram method's functionality.""" - fs = 10e3 - N = 1e5 + fs = 10000 + N = 100000 amp = 2 * np.sqrt(2) noise_power = 0.001 * fs / 2 time = np.arange(N) / fs - freq = np.linspace(1e3, 2e3, N) - x = amp * chirp(time, 1e3, 2.0, 6e3, method='quadratic') + \ - np.random.normal(scale=np.sqrt(noise_power), size=time.shape) + freq = np.linspace(1000, 2000, N) + x = amp * chirp(time, 1000, 2.0, 6000, method="quadratic") + np.random.normal( + scale=np.sqrt(noise_power), size=time.shape + ) f, t, Sxx = spectrogram_lspopt(x, fs, c_parameter=20.0) f_sp, t_sp, Sxx_sp = spectrogram(x, fs) diff --git a/tests/test_utils.py b/tests/test_utils.py index 3831ac1..c5e9817 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -14,22 +14,11 @@ """ -from __future__ import division -from __future__ import print_function -from __future__ import unicode_literals -from __future__ import absolute_import - -import unittest - -import pytest -import six -import numpy as np from numpy.testing import assert_allclose from lspopt import utils - def test_create_lsp_realisation(): """Test that create method is working at least.""" N = 256 @@ -56,7 +45,7 @@ def test_create_lscp_realisation(): def test_create_mlsp_realisation(): """Test that create method is working at least.""" - N = 256 + N = 256 c = [1.4, 20.0] Fs = 8.0 x, H, Rx = utils.create_mlsp_realisation(N, c, Fs)