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
53 changes: 0 additions & 53 deletions .github/workflows/lint.yml

This file was deleted.

30 changes: 23 additions & 7 deletions .github/workflows/pythonpackage.yml
Original file line number Diff line number Diff line change
@@ -1,25 +1,41 @@
name: Python package
name: Test package

on: [push, pull_request]

jobs:
build:
runs-on: ${{ matrix.os }}
defaults:
run:
shell: bash -el {0}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: ["3.10", "3.11", "3.12"]
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
uses: conda-incubator/setup-miniconda@v3
with:
auto-update-conda: true
python-version: ${{ matrix.python-version }}
- name: Install dependencies
activate-environment: numpy-financial-dev
environment-file: environment.yml
auto-activate-base: false
- name: Conda metadata
run: |
python -m pip install --upgrade pip poetry
poetry env use ${{ matrix.python-version }}
poetry install --with=test
conda info
conda list
- name: Lint
run: |
set -euo pipefail
# Tell us what version we are using
ruff version
# Check the source file, ignore type annotations (ANN) for now.
ruff check numpy_financial/ benchmarks/ --ignore F403 --select E,F,B,I
- name: Build project
run: |
spin build -v
- name: Test with pytest
run: |
poetry run pytest --doctest-modules
spin test -- --doctest-modules
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ GTAGS
################
# setup.py working directory
build
build-install
# sphinx build directory
_build
# setup.py dist directory
Expand Down Expand Up @@ -112,3 +113,7 @@ poetry.lock
# Things specific to this project #
###################################
doc/source/_api_stubs

# Misc files that we do not require #
.asv/
.hypothesis/
31 changes: 31 additions & 0 deletions environment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# To use:
# $ conda env create -f environment.yml # `mamba` works too for this command
# $ conda activate numpy-financial-dev
#
name: numpy-financial-dev
channels:
- conda-forge
dependencies:
# Runtime dependencies
- python
- numpy
# Build
- cython>=3.0.9
- compilers
- meson
- meson-python
- ninja
# Tests
- pytest
- pytest-xdist
- asv>=0.6.0
- hypothesis
# Docs
- myst-parser
- numpydoc
- pydata-sphinx-theme>=0.15.0
- spin
# Lint
- ruff>=0.3.0
# Benchmarks
- asv>=0.6.0
19 changes: 19 additions & 0 deletions meson.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
project(
'numpy-financial',
'cython', 'c',
version: '2.0.0.dev',
license: 'BSD-3',
meson_version: '>=1.4.0',
)

cc = meson.get_compiler('c')
cy = meson.get_compiler('cython')

if not cy.version().version_compare('>=3.0.9')
error('NumPy-Financial requires Cython >= 3.0.9')
endif

py = import('python').find_installation(pure: false)
py_dep = py.dependency()

subdir('numpy_financial')
21 changes: 21 additions & 0 deletions numpy_financial/_cfinancial.pyx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from libc.math cimport NAN
cimport cython

@cython.boundscheck(False)
@cython.cdivision(True)
def npv(const double[::1] rates, const double[:, ::1] values, double[:, ::1] out):
cdef:
Py_ssize_t i, j, t
double acc

with nogil:
for i in range(rates.shape[0]):
for j in range(values.shape[0]):
acc = 0.0
for t in range(values.shape[1]):
if rates[i] == -1.0:
acc = NAN
break
else:
acc = acc + values[j, t] / ((1.0 + rates[i]) ** t)
out[i, j] = acc
23 changes: 5 additions & 18 deletions numpy_financial/_financial.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@

from decimal import Decimal

import numba as nb
import numpy as np

from . import _cfinancial

__all__ = ['fv', 'pmt', 'nper', 'ipmt', 'ppmt', 'pv', 'rate',
'irr', 'npv', 'mirr',
'NoRealSolutionError', 'IterationsExceededError']
Expand Down Expand Up @@ -844,20 +845,6 @@ def irr(values, *, raise_exceptions=False):
return eirr[np.argmin(abs_eirr)]


@nb.njit
def _npv_native(rates, values, out):
for i in range(rates.shape[0]):
for j in range(values.shape[0]):
acc = 0.0
for t in range(values.shape[1]):
if rates[i] == -1.0:
acc = np.nan
break
else:
acc += values[j, t] / ((1.0 + rates[i]) ** t)
out[i, j] = acc


def npv(rate, values):
r"""Return the NPV (Net Present Value) of a cash flow series.

Expand Down Expand Up @@ -935,8 +922,8 @@ def npv(rate, values):
[-2798.19, -3612.24],
[-2884.3 , -3710.74]])
"""
values_inner = np.atleast_2d(values)
rate_inner = np.atleast_1d(rate)
values_inner = np.atleast_2d(values).astype(np.float64)
rate_inner = np.atleast_1d(rate).astype(np.float64)

if rate_inner.ndim != 1:
msg = "invalid shape for rates. Rate must be either a scalar or 1d array"
Expand All @@ -948,7 +935,7 @@ def npv(rate, values):

output_shape = _get_output_array_shape(rate_inner, values_inner)
out = np.empty(output_shape)
_npv_native(rate_inner, values_inner, out)
_cfinancial.npv(rate_inner, values_inner, out)
return _ufunc_like(out)


Expand Down
18 changes: 18 additions & 0 deletions numpy_financial/meson.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
py.extension_module(
'_cfinancial',
'_cfinancial.pyx',
install: true,
subdir: 'numpy_financial',
)

python_sources = [
'__init__.py',
'_financial.py',
]

py.install_sources(
python_sources,
subdir: 'numpy_financial',
)

install_subdir('tests', install_dir: py.get_install_dir() / 'numpy_financial')
File renamed without changes.
69 changes: 42 additions & 27 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

build-backend = "mesonpy"
requires = [
"meson-python>=0.15.0",
"Cython>=3.0.9",
"numpy>=1.23.5",
]

[tool.poetry]
[project]
name = "numpy-financial"
version = "2.0.0"
requires-python = ">=3.10"
description = "Simple financial functions"
license = "BSD-3-Clause"
authors = ["Travis E. Oliphant et al."]
Expand Down Expand Up @@ -34,30 +38,41 @@ classifiers = [
"Operating System :: Unix",
"Operating System :: MacOS",
]
packages = [{include = "numpy_financial"}]

[tool.poetry.dependencies]
python = "^3.10"
numpy = "^1.23"
numba = "^0.59.1"

[tool.poetry.group.test.dependencies]
pytest = "^8.0"
hypothesis = {extras = ["numpy"], version = "^6.99.11"}
pytest-xdist = {extras = ["psutil"], version = "^3.5.0"}


[tool.poetry.group.docs.dependencies]
sphinx = "^7.0"
numpydoc = "^1.5"
pydata-sphinx-theme = "^0.15"
myst-parser = "^2.0.0"


[project.optional-dependencies]
test = [
"pytest",
"pytest-xdist",
"hypothesis",
]
doc = [
"sphinx>=7.0",
"numpydoc>=1.5",
"pydata-sphinx-theme>=0.15",
"myst-parser>=2.0.0",
]
dev = [
"ruff>=0.3.0",
"asv>=0.6.0",
]

[tool.poetry.group.lint.dependencies]
ruff = "^0.3"

[tool.spin]
package = 'numpy_financial'

[tool.poetry.group.bench.dependencies]
asv = "^0.6"
[tool.spin.commands]
"Build" = [
"spin.cmds.meson.build",
"spin.cmds.meson.test",
"spin.cmds.build.sdist",
"spin.cmds.pip.install",
]
"Documentation" = [
"spin.cmds.meson.docs"
]
"Environments" = [
"spin.cmds.meson.shell",
"spin.cmds.meson.ipython",
"spin.cmds.meson.python",
"spin.cmds.meson.run"
]
Empty file removed tests/__init__.py
Empty file.