Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/refactor-api' into fix-warnings
Browse files Browse the repository at this point in the history
# Conflicts:
#	psignifit/psigniplot.py
  • Loading branch information
pberkes committed Sep 20, 2024
2 parents c982959 + 85323cf commit cd89116
Show file tree
Hide file tree
Showing 29 changed files with 273 additions and 210 deletions.
36 changes: 36 additions & 0 deletions .github/workflows/cd-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Based on:
# https://packaging.python.org/en/latest/guides/publishing-package-distribution-releases-using-github-actions-ci-cd-workflows/
name: Release

on:
release:
types: [published]

jobs:
build-n-publish:
name: Build and publish Python 🐍 distributions 📦 to PyPI and TestPyPI
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python 3.12
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install pypa/build
run: >-
python -m
pip install
build
--user
- name: Build a binary wheel and a source tarball
run: >-
python -m
build
--sdist
--wheel
--outdir dist/
- name: Publish distribution 📦 to PyPI
if: startsWith(github.ref, 'refs/tags')
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.PYPI_API_TOKEN }}
51 changes: 48 additions & 3 deletions .github/workflows/ci-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python
name: Test

on: [push, pull_request]
on:
push:
pull_request:
schedule: # At 00:00 on day-of-month 1
- cron: '0 0 1 * *'

jobs:
test:
Expand All @@ -14,7 +18,7 @@ jobs:
os: [Ubuntu]
python_version: ['3.10', '3.11', '3.12']
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4

- uses: actions/setup-python@v4
name: Install Python ${{ matrix.python_version }}
Expand All @@ -31,5 +35,46 @@ jobs:
pip install .
- name: Run tests for ${{ matrix.python_version }} through nox
run: |
pytest -vv
pytest -vv
lint:
runs-on: ubuntu-latest
continue-on-error: true
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v4
with:
python-version: "3.12"
- name: flake8 Lint Errors
uses: py-actions/flake8@v2
with:
args: --count --select=E9,F63,F7,F82 --show-source --statistics
# stop the build if there are Python syntax errors or undefined names
# E9: Runtime errors (syntax, indentation, io)
# F63: Wrong use of operators and always-true assertion tests
# F7: Wrong position of statements (break, continue, return, ...)
# F82: Undefined (variable) name
- name: flake8 Lint Warnings
uses: py-actions/flake8@v2
with:
args: --count --exit-zero --statistics --show-source
# print warnings if other style errors are found.
# see .flake8 config file for selected/ignored rules.
# warnings can be found in the action logs

docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v4
with:
python-version: "3.12"
cache: pip
cache-dependency-path: setup.cfg
- name: Install package with docs
run: |
python3 -m pip install --upgrade pip
pip install -e .[docs]
- name: Build the documentation
run: |
cd docs && make html
9 changes: 6 additions & 3 deletions .readthedocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@ version: 2
sphinx:
configuration: docs/conf.py

# Optionally set the version of Python and requirements required to build your docs
build:
os: ubuntu-22.04
tools:
python: "3.10"

python:
version: 3.7
install:
- method: pip
path: .
extra_requirements:
- docs
- docs
3 changes: 1 addition & 2 deletions COPYRIGHT
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Python toolbox for Bayesian psychometric function estimation.

All the code in this package is distributed under the following conditions:

Copyright (c) 2016-2017, psignifit contributors, www.wichmann-lab.org
Copyright (c) 2016-2024, psignifit contributors, www.wichmann-lab.org

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
Expand All @@ -18,4 +18,3 @@ GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.

5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

Python toolbox for Bayesian psychometric function estimation.
[![Tests](https://github.com/wichmann-lab/python-psignifit/actions/workflows/ci-tests.yml/badge.svg)](https://github.com/wichmann-lab/python-psignifit/actions/workflows/ci-tests.yml)
[![Test Coverage](https://codecov.io/gh/wichmann-lab/python-psignifit/branch/master/graph/badge.svg)](https://codecov.io/gh/wichmann-lab/python-psignifit)
[![Documentation Status](https://readthedocs.org/projects/psignifit/badge/?version=latest)](https://psignifit.readthedocs.io/en/latest/?badge=latest)
[![Documentation](https://readthedocs.org/projects/psignifit/badge/?version=latest)](https://psignifit.readthedocs.io/en/latest/?badge=latest)
[![PyPI version](https://img.shields.io/pypi/v/psignifit.svg)](https://pypi.python.org/pypi/psignifit)

## Getting started

Expand All @@ -22,4 +22,3 @@ See the [CONTRIBUTORS](https://github.com/wichmann-lab/python-psignifit/blob/mas
## License and COPYRIGHT

See the [COPYRIGHT](https://github.com/wichmann-lab/python-psignifit/blob/master/COPYRIGHT) file

1 change: 1 addition & 0 deletions docs/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
sg_execution_times.rst
1 change: 1 addition & 0 deletions docs/_static/.gitkeep
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This is just a file to assure git creates this directory still if empty
4 changes: 2 additions & 2 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
# -- Project information -----------------------------------------------------

project = 'psignifit'
author = 'Original: Heiko Schütt, Stefan Harmeling, Jakob Macke, and Felix Wichmann; Python implementation: David-Elias Künstle, Tiziano Zito'
copyright = '2020, ' + author
author = 'psignifit contributors, www.wichmann-lab.org'
copyright = '2024, ' + author


# -- General configuration ---------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion docs/getting_started.rst
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ For example, running

See :ref:`this user guide <plot-functions>` to learn more about the visualizations.

.. figure:: ../images/demo_001_1.png
.. figure:: ./images/demo_001_1.png

Next steps
~~~~~~~~~~
Expand Down
1 change: 1 addition & 0 deletions docs/how_to/How-to-Get-Standard-Parameters.rst
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.. _how-to-get-standard-parameters:

How to: Get Standard Parameters
===============================

Expand Down
1 change: 1 addition & 0 deletions docs/how_to/index.rst
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.. _how-tos:

How-To
======

Expand Down
38 changes: 17 additions & 21 deletions docs/references/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,15 @@ This is the class and function reference of psignifit.

:mod:`psignifit` Main Functions
===============================

.. autosummary::
:toctree: generated/

psignifit.psignifit
psignifit.plot_bias_analysis

:mod:`psignifit` Configration and Result
========================================

.. currentmodule:: psignifit

.. autosummary::
:toctree: generated/

psignifit.configuration.Configuration
psignifit.result.Result
psignifit
pool_blocks
Configuration
Result

:mod:`psignifit.psigniplot` Plotting
====================================
Expand All @@ -34,20 +26,24 @@ This is the class and function reference of psignifit.
.. autosummary::
:toctree: generated/

psigniplot.plot2D
psigniplot.plotMarginal
psigniplot.plotPrior
psigniplot.plotPsych
psigniplot.plotsModelfit
psigniplot.plot_2D_margin
psigniplot.plot_marginal
psigniplot.plot_prior
psigniplot.plot_psychometric_function
psigniplot.plot_modelfit
psigniplot.plot_stimulus_residuals
psigniplot.plot_block_residuals
psigniplot.plot_bias_analysis


:mod:`psignifit.sigmoids` Sigmoids
==================================

.. currentmodule:: psignifit.sigmoids
.. currentmodule:: psignifit

.. autosummary::
:toctree: generated/

psignifit.sigmoids.Sigmoid
psignifit.sigmoids.sigmoid_by_name
psignifit.sigmoids.ALL_SIGMOID_NAMES
sigmoids.Sigmoid
sigmoids.sigmoid_by_name
sigmoids.ALL_SIGMOID_NAMES
38 changes: 2 additions & 36 deletions psignifit/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,39 +9,5 @@
# import here the main function
from .psignifit import psignifit
from ._pooling import pool_blocks
from . import _sigmoids
from .psigniplot import plot_bias_analysis

__name__ = 'psignifit'
__description__ = 'toolbox for Bayesian psychometric function estimation'
__version__ = '4.0a'
__url__ = 'https://github.com/wichmann-lab/python-psignifit'
__author__ = 'psignifit contributors, www.wichmann-lab.org'
__license__ = 'GPLv3+'
__revision__ = 'N/A'

# get the git SHA if we are in a git repo (only useful for devs)

# current dir
CWD = os.path.abspath(os.path.dirname(__file__))
# try two options for getting the git revision
# - nice version with tags
# - plain SHA
for cmd in ('git describe --tags --dirty=+'), ('git rev-parse HEAD'):
try:
proc = subprocess.check_output(cmd.split(),
cwd=CWD,
stderr=subprocess.PIPE,
universal_newlines=True)
__revision__ = proc.strip()
except Exception:
# ok, don't bother
pass


# have a way to test from python
def test():
import pytest
pytest.main([
CWD,
])
from ._configuration import Configuration
from ._result import Result
22 changes: 16 additions & 6 deletions psignifit/_configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
import re
import dataclasses
from typing import Any, Dict, Tuple, Optional, Union
import warnings

from . import _sigmoids
from . import sigmoids
from ._utils import PsignifitException
from ._typing import ExperimentType, Prior

Expand Down Expand Up @@ -48,7 +49,7 @@ class Configuration:
move_bounds: bool = True
pool_max_blocks: int = 25
priors: Optional[Dict[str, Prior]] = dataclasses.field(default=None, hash=False)
sigmoid: Union[str, _sigmoids.Sigmoid] = 'norm'
sigmoid: Union[str, sigmoids.Sigmoid] = 'norm'
stimulus_range: Optional[Tuple[float, float]] = None
thresh_PC: float = 0.5
verbose: bool = True
Expand Down Expand Up @@ -93,6 +94,7 @@ def check_foobar(self, value):
sanity_check_method = getattr(self, sanity_check_name)
attribute_value = getattr(self, attribute.name)
sanity_check_method(attribute_value)
self.check_experiment_type_matches_fixed_parameters(self.fixed_parameters, self.experiment_type)

def check_bounds(self, value):
if value is not None:
Expand Down Expand Up @@ -120,7 +122,7 @@ def check_bounds(self, value):
def check_fixed_parameters(self, value):
if value is not None:
# fixed parameters is a dict in the form {'parameter_name': value}
if type(value) != dict:
if isinstance(type(value), dict):
raise PsignifitException(
f'Option fixed_parameters must be a dictionary ({type(value).__name__} given)!'
)
Expand All @@ -131,6 +133,14 @@ def check_fixed_parameters(self, value):
f'Option fixed_paramters keys must be in {vkeys}. Given {list(value.keys())}!'
)


def check_experiment_type_matches_fixed_parameters(self, fixed_params, experiment_type):
if experiment_type == ExperimentType.N_AFC.value:
if fixed_params is not None and 'gamma' in fixed_params:
warnings.warn(
f'The parameter gamma was fixed to {fixed_params["gamma"]}. In {ExperimentType.N_AFC.value} experiments gamma must be fixed to 1/n. Ignoring fixed gamma.')


def check_experiment_type(self, value):
valid_values = [type.value for type in ExperimentType]
is_valid = value in valid_values
Expand Down Expand Up @@ -212,15 +222,15 @@ def check_width_min(self, value):
except Exception:
raise PsignifitException("Option width_min must be a number")

def make_sigmoid(self) -> _sigmoids.Sigmoid:
def make_sigmoid(self) -> sigmoids.Sigmoid:
""" Construct sigmoid according to this configuration.
Returns:
Sigmoid object with percentage correct and alpha according to config.
"""
if isinstance(self.sigmoid, _sigmoids.Sigmoid):
if isinstance(self.sigmoid, sigmoids.Sigmoid):
self.sigmoid.PC = self.thresh_PC
self.sigmoid.alpha = self.width_alpha
return self.sigmoid
else:
return _sigmoids.sigmoid_by_name(self.sigmoid, PC=self.thresh_PC, alpha=self.width_alpha)
return sigmoids.sigmoid_by_name(self.sigmoid, PC=self.thresh_PC, alpha=self.width_alpha)
12 changes: 8 additions & 4 deletions psignifit/_posterior.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from ._utils import fp_error_handler, PsignifitException
from ._typing import Prior, ParameterGrid
from ._sigmoids import Sigmoid
from .sigmoids import Sigmoid


def integral_weights(grid):
Expand Down Expand Up @@ -242,9 +242,13 @@ def objective(x):
def marginalize_posterior(parameter_grid: ParameterGrid, posterior_mass: np.ndarray) -> Dict[str, np.ndarray]:
marginals = dict()
for i, (param, grid) in enumerate(sorted(parameter_grid.items())):
if grid is None:
if grid is None or len(grid)==1:
marginals[param] = None
axis = tuple(range(0, i)) + tuple(range(i + 1, len(parameter_grid)))
marginals[param] = np.squeeze(posterior_mass.sum(axis))
else:
axis = tuple(range(0, i)) + tuple(range(i + 1, len(parameter_grid)))
# we get first the unnormalized marginal, and then we scale it
nmarginal = np.squeeze(posterior_mass.sum(axis))
integral = np.trapz(nmarginal, x=grid)
marginals[param] = nmarginal / integral

return marginals
Loading

0 comments on commit cd89116

Please sign in to comment.