26 Commits

Author SHA1 Message Date
ef1e687726 Add renovate.json
Some checks failed
gitea-physics/pathfinder/pipeline/pr-master There was a failure building this commit
2022-02-23 19:44:09 +00:00
3a73fee801 More tests with variety of dot ideas
Some checks failed
gitea-physics/pathfinder/pipeline/head There was a failure building this commit
2021-09-13 16:44:07 -05:00
c6a5ee637f Two dipoles, with normalisation
Some checks failed
gitea-physics/pathfinder/pipeline/head There was a failure building this commit
2021-09-13 15:29:02 -05:00
231c5d4b60 Adds a solving
All checks were successful
gitea-physics/pathfinder/pipeline/head This commit looks good
2021-09-13 12:50:00 -05:00
ad0553ce6e Model solution 2021-09-13 12:22:38 -05:00
5726b1d50a Got whole jacobian thing 2021-09-13 11:56:59 -05:00
10b72bfaaa Adds some tests and dot methods and jacobian
All checks were successful
gitea-physics/pathfinder/pipeline/head This commit looks good
2021-09-13 11:51:52 -05:00
69d8befe68 oscillating dipole and arrangement 2021-09-13 11:06:01 -05:00
ca8269b538 Moves static model to its own module
All checks were successful
gitea-physics/pathfinder/pipeline/head This commit looks good
2021-09-06 11:46:11 -05:00
b22a977b9c Adds some tests and lets skip root finding method 2021-09-06 10:02:05 -05:00
6af5d6f78c Adds two dipole tests 2021-08-30 16:08:33 -05:00
60e194853e Adds test for 6 dots and adds some perfect satisfaction checks 2021-08-30 12:25:16 -05:00
2b022927b5 Adds sol method to the model and removes skipped test 2021-08-30 10:31:48 -05:00
ba57e32cc5 Updates mypy for pyproject.toml 2021-08-29 16:58:57 -05:00
4011a5c698 Adds better test for static dipole as well as calculating dot measurements
All checks were successful
gitea-physics/pathfinder/pipeline/head This commit looks good
2021-08-29 15:38:41 -05:00
0e3eddebcc Fixes linting issue and makes html by default
All checks were successful
gitea-physics/pathfinder/pipeline/head This commit looks good
2021-08-29 15:01:54 -05:00
d31fbb55eb Improves some test stuff. 2021-08-29 14:58:52 -05:00
c279f1b470 Removes gradient descent and begins refactor
All checks were successful
gitea-physics/pathfinder/pipeline/head This commit looks good
2021-08-29 10:54:36 -05:00
359e048531 Adds jacobian rows to dot 2021-08-24 14:00:39 -05:00
7230d37244 Provides jenkins build pod
All checks were successful
gitea-physics/pathfinder/pipeline/head This commit looks good
2021-08-23 19:58:23 -05:00
a9c37559f8 Removes empty model
Some checks failed
gitea-physics/pathfinder/pipeline/head There was a failure building this commit
2021-08-23 19:51:33 -05:00
e37849295a Big set of changes, to bring project into more organised state 2021-08-23 19:49:54 -05:00
56e88759fe Includes tests with scipy methods 2021-08-20 07:52:26 -05:00
c51c477579 more robust tests and slight tweaks to algorithm 2021-08-04 19:50:54 -05:00
0c8527704d Return iteration count 2021-08-02 10:10:59 -05:00
026519697b Adds some gradient descent stuff 2021-08-02 08:57:07 -05:00
29 changed files with 1239 additions and 190 deletions

View File

@@ -1,2 +1,3 @@
[flake8]
ignore = W191
ignore = W191, E501
max-line-length = 120

1
.gitignore vendored
View File

@@ -45,6 +45,7 @@ htmlcov/
.cache
nosetests.xml
coverage.xml
pytest.xml
*.cover
*.py,cover
.hypothesis/

73
Jenkinsfile vendored Normal file
View File

@@ -0,0 +1,73 @@
pipeline {
agent {
kubernetes {
label 'pathfinder' // all your pods will be named with this prefix, followed by a unique id
idleMinutes 5 // how long the pod will live after no jobs have run on it
yamlFile 'jenkins/ci-agent-pod.yaml' // path to the pod definition relative to the root of our project
defaultContainer 'python' // define a default container if more than a few stages use it, will default to jnlp container
}
}
options {
parallelsAlwaysFailFast()
}
environment {
POETRY_HOME="/opt/poetry"
POETRY_VERSION="1.1.4"
}
stages {
stage('Build') {
steps {
echo 'Building...'
sh 'python --version'
sh 'curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python'
sh '${POETRY_HOME}/bin/poetry --version'
sh '${POETRY_HOME}/bin/poetry install'
}
}
stage('Test') {
parallel{
stage('pytest') {
steps {
sh '${POETRY_HOME}/bin/poetry run pytest'
}
}
stage('lint') {
steps {
sh '${POETRY_HOME}/bin/poetry run flake8'
}
}
stage('mypy') {
steps {
sh '${POETRY_HOME}/bin/poetry run mypy pathfinder'
}
}
}
}
}
post {
always {
echo 'This will always run'
junit 'pytest.xml'
cobertura coberturaReportFile: 'coverage.xml'
mail (bcc: '',
body: "Project: ${env.JOB_NAME} <br>Build Number: ${env.BUILD_NUMBER} <br> Build URL: ${env.BUILD_URL}", cc: '', charset: 'UTF-8', from: 'jenkins@jenkins.deepak.science', mimeType: 'text/html', replyTo: 'dmallubhotla+jenkins@gmail.com', subject: "${env.JOB_NAME} #${env.BUILD_NUMBER}: Build ${currentBuild.currentResult}", to: "dmallubhotla+ci@gmail.com")
}
success {
echo 'This will run only if successful'
}
failure {
echo 'This will run only if failed'
}
unstable {
echo 'This will run only if the run was marked as unstable'
}
changed {
echo 'This will run only if the state of the Pipeline has changed'
echo 'For example, if the Pipeline was previously failing but is now successful'
}
}
}

4
do.sh
View File

@@ -16,6 +16,10 @@ test() {
poetry run pytest
}
htmlcov() {
poetry run pytest --cov-report=html
}
all() {
build && test
}

14
jenkins/ci-agent-pod.yaml Normal file
View File

@@ -0,0 +1,14 @@
apiVersion: v1
kind: Pod
spec:
containers: # list of containers that you want present for your build, you can define a default container in the Jenkinsfile
- name: python
image: python:3.8
command: ["tail", "-f", "/dev/null"] # this or any command that is bascially a noop is required, this is so that you don't overwrite the entrypoint of the base container
imagePullPolicy: Always # use cache or pull image for agent
resources: # limits the resources your build contaienr
requests:
memory: "2Gi"
cpu: "500m"
limits:
memory: "2Gi"

View File

@@ -0,0 +1,5 @@
from pathfinder.model.oscillating.oscillating_dipole import OscillatingDipole, OscillatingDipoleArrangement
from pathfinder.model.oscillating.dot import DotMeasurement
from pathfinder.model.oscillating.model import DotOscillatingDipoleModel
__all__ = ['DotMeasurement', 'OscillatingDipole', 'OscillatingDipoleArrangement', 'DotOscillatingDipoleModel']

View File

@@ -0,0 +1,87 @@
from dataclasses import dataclass
import numpy
import numpy.typing
@dataclass
class DotMeasurement():
'''
Representation of a dot measuring oscillating dipoles.
Parameters
----------
v : float
The voltage measured at the dot.
r : numpy.ndarray
The position of the dot.
f : float
The measurement frequency.
'''
v: float
r: numpy.ndarray
f: float
def __post_init__(self) -> None:
self.r = numpy.array(self.r)
def v_for_point(self, pt: numpy.ndarray) -> float:
'''
Returns the voltage for an oscillating dipole represented as a phase space point.
Parameters
----------
pt: A length 7 numpy array representing a dipole, as (px, py, pz, sx, sy, sz, w).
Returns
----------
Returns the voltage from that dipole at the point.
'''
p = pt[0:3] # hardcoded here because chances
s = pt[3:6] # are we'll only ever work in 3d.
w = pt[6]
return (self._alpha(p, s))**2 * self._b(w)
def _alpha(self, p: numpy.ndarray, s: numpy.ndarray) -> float:
diff = self.r - s
return p.dot(diff) / (numpy.linalg.norm(diff)**3)
def _b(self, w: float) -> float:
return (1 / numpy.pi) * (w / (self.f**2 + w**2))
def cost(self, pts: numpy.ndarray) -> float:
# 7 because dipole in 3d has 7 degrees of freedom.
pt_length = 7
# creates numpy.ndarrays in groups of pt_length.
# Will throw problems for irregular points, but that's okay for now.
chunked_pts = [pts[i: i + pt_length] for i in range(0, len(pts), pt_length)]
return sum(self.v_for_point(pt) for pt in chunked_pts) - self.v
def jac_pt(self, pt: numpy.ndarray) -> numpy.ndarray:
p = pt[0:3] # hardcoded here because chances
s = pt[3:6] # are we'll only ever work in 3d.
w = pt[6]
diff = self.r - s
alpha = self._alpha(p, s)
b = self._b(w)
p_divs = 2 * alpha * diff / (numpy.linalg.norm(diff)**3) * b
r_divs = (-p / (numpy.linalg.norm(diff)**3) + 3 * p.dot(diff) * diff / (numpy.linalg.norm(diff)**5)) * 2 * alpha * b
f2 = self.f**2
w2 = w**2
w_div = alpha**2 * (1 / numpy.pi) * ((f2 - w2) / ((f2 + w2)**2))
return numpy.concatenate((p_divs, r_divs, w_div), axis=None)
def jac(self, pts: numpy.ndarray) -> numpy.ndarray:
# 7 because oscillating dipole in 3d has 7 degrees of freedom.
pt_length = 7
# creates numpy.ndarrays in groups of pt_length.
# Will throw problems for irregular points, but that's okay for now.
chunked_pts = [pts[i: i + pt_length] for i in range(0, len(pts), pt_length)]
return numpy.append([], [self.jac_pt(pt) for pt in chunked_pts])

View File

@@ -0,0 +1,45 @@
from typing import Callable, Sequence
import numpy
import scipy.optimize
from pathfinder.model.oscillating.dot import DotMeasurement
import pathfinder.model.oscillating.util
class DotOscillatingDipoleModel():
'''
Model of n static oscillating with a collection of voltage measurements
at dots at different positions and frequencies.
Parameters
----------
dots : Sequence[DotMeasurement]
A collection of dots representing a series of measured noise spectrum values.
n: int
The number of dipoles to assume.
'''
def __init__(self, dots: Sequence[DotMeasurement], n: int) -> None:
self.dots = dots
self.m = len(dots)
self.n = n
def __repr__(self) -> str:
return f'DotOscillatingDipoleModel({repr(list(self.dots))}, {self.n})'
def costs(self) -> Callable[[numpy.ndarray], numpy.ndarray]:
def costs_to_return(pt: numpy.ndarray) -> numpy.ndarray:
return numpy.array([dot.cost(pt) for dot in self.dots])
return costs_to_return
def jac(self) -> Callable[[numpy.ndarray], numpy.ndarray]:
def jac_to_return(pts: numpy.ndarray) -> numpy.ndarray:
return numpy.array([dot.jac(pts) for dot in self.dots])
return jac_to_return
def sol(self, initial_dipole=(0.1, 0.1, 0.1), initial_position=(.1, .1, .1), initial_frequency=1, use_root=True):
initial = numpy.tile(numpy.concatenate((initial_dipole, initial_position, initial_frequency), axis=None), self.n)
result = scipy.optimize.least_squares(self.costs(), initial, jac=self.jac(), ftol=1e-15, gtol=3e-16)
result.pathfinder_x = pathfinder.model.oscillating.util.normalize_oscillating_dipole_list(result.x)
return result

View File

@@ -0,0 +1,77 @@
from dataclasses import dataclass
import numpy
import numpy.typing
from typing import Sequence, List, Tuple
from pathfinder.model.oscillating.dot import DotMeasurement
DotInput = Tuple[numpy.typing.ArrayLike, float]
@dataclass
class OscillatingDipole():
'''
Representation of an oscilltaing dipole, either known or guessed.
Parameters
----------
p : numpy.ndarray
The oscillating dipole moment, with overall sign arbitrary.
s : numpy.ndarray
The position of the dipole.
w : float
The oscillation frequency.
'''
p: numpy.ndarray
s: numpy.ndarray
w: float
def __post_init__(self) -> None:
'''
Coerce the inputs into numpy arrays.
'''
self.p = numpy.array(self.p)
self.s = numpy.array(self.s)
def s_at_position(self, r: numpy.ndarray, f: float) -> float:
'''
Returns the noise potential at a point r, at some frequency f.
Parameters
----------
r : numpy.ndarray
The position of the dot.
f : float
The dot frequency to sample.
'''
return (self._alpha(r))**2 * self._b(f)
def _alpha(self, r: numpy.ndarray) -> float:
diff = r - self.s
return self.p.dot(diff) / (numpy.linalg.norm(diff)**3)
def _b(self, f: float) -> float:
return (1 / numpy.pi) * (self.w / (f**2 + self.w**2))
class OscillatingDipoleArrangement():
'''
A collection of oscillating dipoles, which we are interested in being able to characterise.
Parameters
--------
dipoles : Sequence[OscillatingDipole]
'''
def __init__(self, dipoles: Sequence[OscillatingDipole]):
self.dipoles = dipoles
def get_dot_measurement(self, dot_input: DotInput) -> DotMeasurement:
r = numpy.array(dot_input[0])
f = dot_input[1]
return DotMeasurement(sum([dipole.s_at_position(r, f) for dipole in self.dipoles]), r, f)
def get_dot_measurements(self, dot_inputs: Sequence[DotInput]) -> List[DotMeasurement]:
'''
For a series of points, each with three coordinates and a frequency, return a list of the corresponding DotMeasurements.
'''
return [self.get_dot_measurement(dot_input) for dot_input in dot_inputs]

View File

@@ -0,0 +1,19 @@
import numpy
import operator
# flips px, py, pz
SIGN_ARRAY = numpy.array((-1, -1, -1, 1, 1, 1, 1))
def flip_chunk_to_positive_px(pt: numpy.ndarray) -> numpy.ndarray:
if pt[0] > 0:
return pt
else:
return SIGN_ARRAY * pt
def normalize_oscillating_dipole_list(pts: numpy.ndarray) -> numpy.ndarray:
pt_length = 7
chunked_pts = [flip_chunk_to_positive_px(pts[i: i + pt_length]) for i in range(0, len(pts), pt_length)]
return numpy.concatenate(sorted(chunked_pts, key=lambda x: tuple(round(val, 3) for val in operator.itemgetter(0, 1, 2, 3, 4, 5, 6)(x))), axis=None)

View File

@@ -0,0 +1,5 @@
from pathfinder.model.static.dot import DotMeasurement
from pathfinder.model.static.model import DotDipoleModel
from pathfinder.model.static.staticdipole import StaticDipoleArrangement, StaticDipole
__all__ = ['DotMeasurement', 'DotDipoleModel', 'StaticDipoleArrangement', 'StaticDipole']

View File

@@ -0,0 +1,69 @@
from dataclasses import dataclass
import numpy
import numpy.typing
@dataclass
class DotMeasurement():
'''
Representation of a dot measuring static dipoles.
Parameters
----------
v : float
The voltage measured at the dot.
r : numpy.ndarray
The position of the dot.
'''
v: float
r: numpy.ndarray
def __post_init__(self) -> None:
self.r = numpy.array(self.r)
def v_for_point(self, pt: numpy.ndarray) -> float:
'''
Returns the voltage for a static dipole represented as a phase space point.
Parameters
----------
pt: A length 6 numpy array representing a dipole, as (px, py, pz, sx, sy, sz).
Returns
----------
Returns the voltage from that dipole at the point.
'''
p = pt[0:3] # hardcoded here because chances
s = pt[3:6] # are we'll only ever work in 3d.
diff = self.r - s
return p.dot(diff) / (numpy.linalg.norm(diff)**3)
def cost(self, pts: numpy.ndarray) -> float:
# 6 because dipole in 3d has 6 degrees of freedom.
pt_length = 6
# creates numpy.ndarrays in groups of pt_length.
# Will throw problems for irregular points, but that's okay for now.
chunked_pts = [pts[i: i + pt_length] for i in range(0, len(pts), pt_length)]
return sum(self.v_for_point(pt) for pt in chunked_pts) - self.v
def jac_pt(self, pt: numpy.ndarray) -> numpy.ndarray:
p = pt[0:3] # hardcoded here because chances
s = pt[3:6] # are we'll only ever work in 3d.
diff = self.r - s
p_divs = diff / (numpy.linalg.norm(diff)**3)
r_divs = -p / (numpy.linalg.norm(diff)**3) + 3 * p.dot(diff) * diff / (numpy.linalg.norm(diff)**5)
return numpy.append(p_divs, r_divs)
def jac(self, pts: numpy.ndarray) -> numpy.ndarray:
# 6 because dipole in 3d has 6 degrees of freedom.
pt_length = 6
# creates numpy.ndarrays in groups of pt_length.
# Will throw problems for irregular points, but that's okay for now.
chunked_pts = [pts[i: i + pt_length] for i in range(0, len(pts), pt_length)]
return numpy.append([], [self.jac_pt(pt) for pt in chunked_pts])

View File

@@ -0,0 +1,44 @@
from typing import Callable, Sequence
import numpy
import scipy.optimize
from pathfinder.model.static.dot import DotMeasurement
class DotDipoleModel():
'''
Model of n static dipoles with a collection of voltage measurements
at dots at different positions.
Parameters
----------
dots : Sequence[Dot]
A collection of dots representing a series of measured voltages.
n: int
The number of dipoles to assume.
'''
def __init__(self, dots: Sequence[DotMeasurement], n: int) -> None:
self.dots = dots
self.m = len(dots)
self.n = n
def __repr__(self) -> str:
return f'DotDipoleModel({repr(list(self.dots))}, {self.n})'
def costs(self) -> Callable[[numpy.ndarray], numpy.ndarray]:
def costs_to_return(pt: numpy.ndarray) -> numpy.ndarray:
return numpy.array([dot.cost(pt) for dot in self.dots])
return costs_to_return
def jac(self) -> Callable[[numpy.ndarray], numpy.ndarray]:
def jac_to_return(pts: numpy.ndarray) -> numpy.ndarray:
return numpy.array([dot.jac(pts) for dot in self.dots])
return jac_to_return
def sol(self, initial_dipole=(0.1, 0.1, 0.1), initial_position=(.1, .1, .1), use_root=True):
initial = numpy.tile(numpy.append(initial_dipole, initial_position), self.n)
if use_root and self.m == 6 * self.n:
# We are perfectly specified
return scipy.optimize.root(self.costs(), initial, jac=self.jac(), tol=1e-12)
return scipy.optimize.least_squares(self.costs(), initial, jac=self.jac(), ftol=1e-15, gtol=3e-16)

View File

@@ -0,0 +1,58 @@
from dataclasses import dataclass
import numpy
import numpy.typing
from typing import Sequence, List
from pathfinder.model.static.dot import DotMeasurement
@dataclass
class StaticDipole():
'''
Representation of a static dipoles, either known or guessed.
Parameters
----------
p : numpy.ndarray
The static dipole moment.
s : numpy.ndarray
The position of the dipole.
'''
p: numpy.ndarray
s: numpy.ndarray
def __post_init__(self) -> None:
'''
Coerce the inputs into numpy arrays.
'''
self.p = numpy.array(self.p)
self.s = numpy.array(self.s)
def v_at_position(self, r: numpy.ndarray) -> float:
'''
Mirrors the same function in pathfinder.model.dot.
'''
diff = r - self.s
return self.p.dot(diff) / (numpy.linalg.norm(diff)**3)
class StaticDipoleArrangement():
'''
A collection of static dipoles, which we are interested in being able to characterise.
Parameters
--------
dipoles : Sequence[StaticDipole]
'''
def __init__(self, dipoles):
self.dipoles = dipoles
def get_dot_measurement(self, dot_position: numpy.typing.ArrayLike) -> DotMeasurement:
r = numpy.array(dot_position)
return DotMeasurement(sum([dipole.v_at_position(r) for dipole in self.dipoles]), r)
def get_dot_measurements(self, dot_positions: Sequence[numpy.typing.ArrayLike]) -> List[DotMeasurement]:
'''
For a series of points, each with three coordinates, return
'''
return [self.get_dot_measurement(dot_position) for dot_position in dot_positions]

376
poetry.lock generated
View File

@@ -1,215 +1,196 @@
[[package]]
category = "dev"
description = "Atomic file writes."
marker = "sys_platform == \"win32\""
name = "atomicwrites"
version = "1.4.0"
description = "Atomic file writes."
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "1.4.0"
[[package]]
category = "dev"
description = "Classes Without Boilerplate"
name = "attrs"
version = "21.2.0"
description = "Classes Without Boilerplate"
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
version = "21.2.0"
[package.extras]
dev = ["coverage (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit"]
dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit"]
docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"]
tests = ["coverage (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface"]
tests_no_zope = ["coverage (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"]
tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface"]
tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"]
[[package]]
category = "dev"
description = "Cross-platform colored terminal text."
marker = "sys_platform == \"win32\""
name = "colorama"
version = "0.4.4"
description = "Cross-platform colored terminal text."
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
version = "0.4.4"
[[package]]
category = "dev"
description = "Code coverage measurement for Python"
name = "coverage"
version = "5.5"
description = "Code coverage measurement for Python"
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4"
version = "5.5"
[package.extras]
toml = ["toml"]
[[package]]
category = "dev"
description = "the modular source code checker: pep8 pyflakes and co"
name = "flake8"
version = "3.9.2"
description = "the modular source code checker: pep8 pyflakes and co"
category = "dev"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
version = "3.9.2"
[package.dependencies]
mccabe = ">=0.6.0,<0.7.0"
pycodestyle = ">=2.7.0,<2.8.0"
pyflakes = ">=2.3.0,<2.4.0"
[package.dependencies.importlib-metadata]
python = "<3.8"
version = "*"
[[package]]
category = "dev"
description = "Read metadata from Python packages"
marker = "python_version < \"3.8\""
name = "importlib-metadata"
optional = false
python-versions = ">=3.6"
version = "4.6.1"
[package.dependencies]
zipp = ">=0.5"
[package.dependencies.typing-extensions]
python = "<3.8"
version = ">=3.6.4"
[package.extras]
docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
perf = ["ipython"]
testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"]
[[package]]
category = "dev"
description = "iniconfig: brain-dead simple config-ini parsing"
name = "iniconfig"
optional = false
python-versions = "*"
version = "1.1.1"
[[package]]
description = "iniconfig: brain-dead simple config-ini parsing"
category = "dev"
description = "McCabe checker, plugin for flake8"
name = "mccabe"
optional = false
python-versions = "*"
version = "0.6.1"
[[package]]
name = "mccabe"
version = "0.6.1"
description = "McCabe checker, plugin for flake8"
category = "dev"
optional = false
python-versions = "*"
[[package]]
name = "more-itertools"
version = "8.8.0"
description = "More routines for operating on iterables, beyond itertools"
category = "main"
optional = false
python-versions = ">=3.5"
[[package]]
name = "mypy"
version = "0.910"
description = "Optional static typing for Python"
category = "dev"
description = "Optional static typing for Python"
name = "mypy"
optional = false
python-versions = ">=3.5"
version = "0.790"
[package.dependencies]
mypy-extensions = ">=0.4.3,<0.5.0"
typed-ast = ">=1.4.0,<1.5.0"
toml = "*"
typing-extensions = ">=3.7.4"
[package.extras]
dmypy = ["psutil (>=4.0)"]
python2 = ["typed-ast (>=1.4.0,<1.5.0)"]
[[package]]
category = "dev"
description = "Experimental type system extensions for programs checked with the mypy typechecker."
name = "mypy-extensions"
version = "0.4.3"
description = "Experimental type system extensions for programs checked with the mypy typechecker."
category = "dev"
optional = false
python-versions = "*"
version = "0.4.3"
[[package]]
category = "dev"
description = "Core utilities for Python packages"
name = "numpy"
version = "1.21.1"
description = "NumPy is the fundamental package for array computing with Python."
category = "main"
optional = false
python-versions = ">=3.7"
[[package]]
name = "packaging"
version = "21.0"
description = "Core utilities for Python packages"
category = "dev"
optional = false
python-versions = ">=3.6"
version = "21.0"
[package.dependencies]
pyparsing = ">=2.0.2"
[[package]]
category = "dev"
description = "plugin and hook calling mechanisms for python"
name = "pluggy"
version = "0.13.1"
description = "plugin and hook calling mechanisms for python"
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "0.13.1"
[package.dependencies]
[package.dependencies.importlib-metadata]
python = "<3.8"
version = ">=0.12"
[package.extras]
dev = ["pre-commit", "tox"]
[[package]]
category = "dev"
description = "library with cross-python path, ini-parsing, io, code, log facilities"
name = "py"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "1.10.0"
description = "library with cross-python path, ini-parsing, io, code, log facilities"
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[[package]]
category = "dev"
description = "Python style guide checker"
name = "pycodestyle"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "2.7.0"
[[package]]
description = "Python style guide checker"
category = "dev"
description = "passive checker of Python programs"
name = "pyflakes"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "2.3.1"
[[package]]
name = "pyflakes"
version = "2.3.1"
description = "passive checker of Python programs"
category = "dev"
description = "Python parsing module"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[[package]]
name = "pyparsing"
version = "2.4.7"
description = "Python parsing module"
category = "dev"
optional = false
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
version = "2.4.7"
[[package]]
category = "dev"
description = "pytest: simple powerful testing with Python"
name = "pytest"
version = "6.2.4"
description = "pytest: simple powerful testing with Python"
category = "dev"
optional = false
python-versions = ">=3.6"
version = "6.2.4"
[package.dependencies]
atomicwrites = ">=1.0"
atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""}
attrs = ">=19.2.0"
colorama = "*"
colorama = {version = "*", markers = "sys_platform == \"win32\""}
iniconfig = "*"
packaging = "*"
pluggy = ">=0.12,<1.0.0a1"
py = ">=1.8.2"
toml = "*"
[package.dependencies.importlib-metadata]
python = "<3.8"
version = ">=0.12"
[package.extras]
testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"]
[[package]]
category = "dev"
description = "Pytest plugin for measuring coverage."
name = "pytest-cov"
version = "2.12.1"
description = "Pytest plugin for measuring coverage."
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
version = "2.12.1"
[package.dependencies]
coverage = ">=5.2.1"
@@ -220,45 +201,36 @@ toml = "*"
testing = ["fields", "hunter", "process-tests", "six", "pytest-xdist", "virtualenv"]
[[package]]
category = "dev"
description = "Python Library for Tom's Obvious, Minimal Language"
name = "toml"
optional = false
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
version = "0.10.2"
[[package]]
category = "dev"
description = "a fork of Python 2 and 3 ast modules with type comment support"
name = "typed-ast"
optional = false
python-versions = "*"
version = "1.4.3"
[[package]]
category = "dev"
description = "Backported and Experimental Type Hints for Python 3.5+"
name = "typing-extensions"
optional = false
python-versions = "*"
version = "3.10.0.0"
[[package]]
category = "dev"
description = "Backport of pathlib-compatible object wrapper for zip files"
marker = "python_version < \"3.8\""
name = "zipp"
name = "scipy"
version = "1.5.4"
description = "SciPy: Scientific Library for Python"
category = "main"
optional = false
python-versions = ">=3.6"
version = "3.5.0"
[package.extras]
docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"]
[package.dependencies]
numpy = ">=1.14.5"
[[package]]
name = "toml"
version = "0.10.2"
description = "Python Library for Tom's Obvious, Minimal Language"
category = "dev"
optional = false
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
[[package]]
name = "typing-extensions"
version = "3.10.0.0"
description = "Backported and Experimental Type Hints for Python 3.5+"
category = "dev"
optional = false
python-versions = "*"
[metadata]
content-hash = "b3de1ca8a251edc8dae2c93e878deb3131846055ac2a6d6b34e43ea20a8f1ea2"
python-versions = "^3.7"
lock-version = "1.1"
python-versions = "^3.8,<3.10"
content-hash = "daffa6e91d6921845be603d5c0565d1afb511a57bb9a2f4b9c1084754b7a5314"
[metadata.files]
atomicwrites = [
@@ -331,10 +303,6 @@ flake8 = [
{file = "flake8-3.9.2-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"},
{file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"},
]
importlib-metadata = [
{file = "importlib_metadata-4.6.1-py3-none-any.whl", hash = "sha256:9f55f560e116f8643ecf2922d9cd3e1c7e8d52e683178fecd9d08f6aa357e11e"},
{file = "importlib_metadata-4.6.1.tar.gz", hash = "sha256:079ada16b7fc30dfbb5d13399a5113110dab1aa7c2bc62f66af75f0b717c8cac"},
]
iniconfig = [
{file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
{file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"},
@@ -343,26 +311,69 @@ mccabe = [
{file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"},
{file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"},
]
more-itertools = [
{file = "more-itertools-8.8.0.tar.gz", hash = "sha256:83f0308e05477c68f56ea3a888172c78ed5d5b3c282addb67508e7ba6c8f813a"},
{file = "more_itertools-8.8.0-py3-none-any.whl", hash = "sha256:2cf89ec599962f2ddc4d568a05defc40e0a587fbc10d5989713638864c36be4d"},
]
mypy = [
{file = "mypy-0.790-cp35-cp35m-macosx_10_6_x86_64.whl", hash = "sha256:bd03b3cf666bff8d710d633d1c56ab7facbdc204d567715cb3b9f85c6e94f669"},
{file = "mypy-0.790-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:2170492030f6faa537647d29945786d297e4862765f0b4ac5930ff62e300d802"},
{file = "mypy-0.790-cp35-cp35m-win_amd64.whl", hash = "sha256:e86bdace26c5fe9cf8cb735e7cedfe7850ad92b327ac5d797c656717d2ca66de"},
{file = "mypy-0.790-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e97e9c13d67fbe524be17e4d8025d51a7dca38f90de2e462243ab8ed8a9178d1"},
{file = "mypy-0.790-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0d34d6b122597d48a36d6c59e35341f410d4abfa771d96d04ae2c468dd201abc"},
{file = "mypy-0.790-cp36-cp36m-win_amd64.whl", hash = "sha256:72060bf64f290fb629bd4a67c707a66fd88ca26e413a91384b18db3876e57ed7"},
{file = "mypy-0.790-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:eea260feb1830a627fb526d22fbb426b750d9f5a47b624e8d5e7e004359b219c"},
{file = "mypy-0.790-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:c614194e01c85bb2e551c421397e49afb2872c88b5830e3554f0519f9fb1c178"},
{file = "mypy-0.790-cp37-cp37m-win_amd64.whl", hash = "sha256:0a0d102247c16ce93c97066443d11e2d36e6cc2a32d8ccc1f705268970479324"},
{file = "mypy-0.790-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cf4e7bf7f1214826cf7333627cb2547c0db7e3078723227820d0a2490f117a01"},
{file = "mypy-0.790-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:af4e9ff1834e565f1baa74ccf7ae2564ae38c8df2a85b057af1dbbc958eb6666"},
{file = "mypy-0.790-cp38-cp38-win_amd64.whl", hash = "sha256:da56dedcd7cd502ccd3c5dddc656cb36113dd793ad466e894574125945653cea"},
{file = "mypy-0.790-py3-none-any.whl", hash = "sha256:2842d4fbd1b12ab422346376aad03ff5d0805b706102e475e962370f874a5122"},
{file = "mypy-0.790.tar.gz", hash = "sha256:2b21ba45ad9ef2e2eb88ce4aeadd0112d0f5026418324176fd494a6824b74975"},
{file = "mypy-0.910-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:a155d80ea6cee511a3694b108c4494a39f42de11ee4e61e72bc424c490e46457"},
{file = "mypy-0.910-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:b94e4b785e304a04ea0828759172a15add27088520dc7e49ceade7834275bedb"},
{file = "mypy-0.910-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:088cd9c7904b4ad80bec811053272986611b84221835e079be5bcad029e79dd9"},
{file = "mypy-0.910-cp35-cp35m-win_amd64.whl", hash = "sha256:adaeee09bfde366d2c13fe6093a7df5df83c9a2ba98638c7d76b010694db760e"},
{file = "mypy-0.910-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:ecd2c3fe726758037234c93df7e98deb257fd15c24c9180dacf1ef829da5f921"},
{file = "mypy-0.910-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:d9dd839eb0dc1bbe866a288ba3c1afc33a202015d2ad83b31e875b5905a079b6"},
{file = "mypy-0.910-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:3e382b29f8e0ccf19a2df2b29a167591245df90c0b5a2542249873b5c1d78212"},
{file = "mypy-0.910-cp36-cp36m-win_amd64.whl", hash = "sha256:53fd2eb27a8ee2892614370896956af2ff61254c275aaee4c230ae771cadd885"},
{file = "mypy-0.910-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b6fb13123aeef4a3abbcfd7e71773ff3ff1526a7d3dc538f3929a49b42be03f0"},
{file = "mypy-0.910-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e4dab234478e3bd3ce83bac4193b2ecd9cf94e720ddd95ce69840273bf44f6de"},
{file = "mypy-0.910-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:7df1ead20c81371ccd6091fa3e2878559b5c4d4caadaf1a484cf88d93ca06703"},
{file = "mypy-0.910-cp37-cp37m-win_amd64.whl", hash = "sha256:0aadfb2d3935988ec3815952e44058a3100499f5be5b28c34ac9d79f002a4a9a"},
{file = "mypy-0.910-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ec4e0cd079db280b6bdabdc807047ff3e199f334050db5cbb91ba3e959a67504"},
{file = "mypy-0.910-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:119bed3832d961f3a880787bf621634ba042cb8dc850a7429f643508eeac97b9"},
{file = "mypy-0.910-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:866c41f28cee548475f146aa4d39a51cf3b6a84246969f3759cb3e9c742fc072"},
{file = "mypy-0.910-cp38-cp38-win_amd64.whl", hash = "sha256:ceb6e0a6e27fb364fb3853389607cf7eb3a126ad335790fa1e14ed02fba50811"},
{file = "mypy-0.910-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1a85e280d4d217150ce8cb1a6dddffd14e753a4e0c3cf90baabb32cefa41b59e"},
{file = "mypy-0.910-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:42c266ced41b65ed40a282c575705325fa7991af370036d3f134518336636f5b"},
{file = "mypy-0.910-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:3c4b8ca36877fc75339253721f69603a9c7fdb5d4d5a95a1a1b899d8b86a4de2"},
{file = "mypy-0.910-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:c0df2d30ed496a08de5daed2a9ea807d07c21ae0ab23acf541ab88c24b26ab97"},
{file = "mypy-0.910-cp39-cp39-win_amd64.whl", hash = "sha256:c6c2602dffb74867498f86e6129fd52a2770c48b7cd3ece77ada4fa38f94eba8"},
{file = "mypy-0.910-py3-none-any.whl", hash = "sha256:ef565033fa5a958e62796867b1df10c40263ea9ded87164d67572834e57a174d"},
{file = "mypy-0.910.tar.gz", hash = "sha256:704098302473cb31a218f1775a873b376b30b4c18229421e9e9dc8916fd16150"},
]
mypy-extensions = [
{file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"},
{file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"},
]
numpy = [
{file = "numpy-1.21.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:38e8648f9449a549a7dfe8d8755a5979b45b3538520d1e735637ef28e8c2dc50"},
{file = "numpy-1.21.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:fd7d7409fa643a91d0a05c7554dd68aa9c9bb16e186f6ccfe40d6e003156e33a"},
{file = "numpy-1.21.1-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a75b4498b1e93d8b700282dc8e655b8bd559c0904b3910b144646dbbbc03e062"},
{file = "numpy-1.21.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1412aa0aec3e00bc23fbb8664d76552b4efde98fb71f60737c83efbac24112f1"},
{file = "numpy-1.21.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e46ceaff65609b5399163de5893d8f2a82d3c77d5e56d976c8b5fb01faa6b671"},
{file = "numpy-1.21.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:c6a2324085dd52f96498419ba95b5777e40b6bcbc20088fddb9e8cbb58885e8e"},
{file = "numpy-1.21.1-cp37-cp37m-win32.whl", hash = "sha256:73101b2a1fef16602696d133db402a7e7586654682244344b8329cdcbbb82172"},
{file = "numpy-1.21.1-cp37-cp37m-win_amd64.whl", hash = "sha256:7a708a79c9a9d26904d1cca8d383bf869edf6f8e7650d85dbc77b041e8c5a0f8"},
{file = "numpy-1.21.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:95b995d0c413f5d0428b3f880e8fe1660ff9396dcd1f9eedbc311f37b5652e16"},
{file = "numpy-1.21.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:635e6bd31c9fb3d475c8f44a089569070d10a9ef18ed13738b03049280281267"},
{file = "numpy-1.21.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4a3d5fb89bfe21be2ef47c0614b9c9c707b7362386c9a3ff1feae63e0267ccb6"},
{file = "numpy-1.21.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:8a326af80e86d0e9ce92bcc1e65c8ff88297de4fa14ee936cb2293d414c9ec63"},
{file = "numpy-1.21.1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:791492091744b0fe390a6ce85cc1bf5149968ac7d5f0477288f78c89b385d9af"},
{file = "numpy-1.21.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0318c465786c1f63ac05d7c4dbcecd4d2d7e13f0959b01b534ea1e92202235c5"},
{file = "numpy-1.21.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9a513bd9c1551894ee3d31369f9b07460ef223694098cf27d399513415855b68"},
{file = "numpy-1.21.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:91c6f5fc58df1e0a3cc0c3a717bb3308ff850abdaa6d2d802573ee2b11f674a8"},
{file = "numpy-1.21.1-cp38-cp38-win32.whl", hash = "sha256:978010b68e17150db8765355d1ccdd450f9fc916824e8c4e35ee620590e234cd"},
{file = "numpy-1.21.1-cp38-cp38-win_amd64.whl", hash = "sha256:9749a40a5b22333467f02fe11edc98f022133ee1bfa8ab99bda5e5437b831214"},
{file = "numpy-1.21.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d7a4aeac3b94af92a9373d6e77b37691b86411f9745190d2c351f410ab3a791f"},
{file = "numpy-1.21.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d9e7912a56108aba9b31df688a4c4f5cb0d9d3787386b87d504762b6754fbb1b"},
{file = "numpy-1.21.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:25b40b98ebdd272bc3020935427a4530b7d60dfbe1ab9381a39147834e985eac"},
{file = "numpy-1.21.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:8a92c5aea763d14ba9d6475803fc7904bda7decc2a0a68153f587ad82941fec1"},
{file = "numpy-1.21.1-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:05a0f648eb28bae4bcb204e6fd14603de2908de982e761a2fc78efe0f19e96e1"},
{file = "numpy-1.21.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f01f28075a92eede918b965e86e8f0ba7b7797a95aa8d35e1cc8821f5fc3ad6a"},
{file = "numpy-1.21.1-cp39-cp39-win32.whl", hash = "sha256:88c0b89ad1cc24a5efbb99ff9ab5db0f9a86e9cc50240177a571fbe9c2860ac2"},
{file = "numpy-1.21.1-cp39-cp39-win_amd64.whl", hash = "sha256:01721eefe70544d548425a07c80be8377096a54118070b8a62476866d5208e33"},
{file = "numpy-1.21.1-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2d4d1de6e6fb3d28781c73fbde702ac97f03d79e4ffd6598b880b2d95d62ead4"},
{file = "numpy-1.21.1.zip", hash = "sha256:dff4af63638afcc57a3dfb9e4b26d434a7a602d225b42d746ea7fe2edf1342fd"},
]
packaging = [
{file = "packaging-21.0-py3-none-any.whl", hash = "sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14"},
{file = "packaging-21.0.tar.gz", hash = "sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7"},
@@ -395,48 +406,39 @@ pytest-cov = [
{file = "pytest-cov-2.12.1.tar.gz", hash = "sha256:261ceeb8c227b726249b376b8526b600f38667ee314f910353fa318caa01f4d7"},
{file = "pytest_cov-2.12.1-py2.py3-none-any.whl", hash = "sha256:261bb9e47e65bd099c89c3edf92972865210c36813f80ede5277dceb77a4a62a"},
]
scipy = [
{file = "scipy-1.5.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:4f12d13ffbc16e988fa40809cbbd7a8b45bc05ff6ea0ba8e3e41f6f4db3a9e47"},
{file = "scipy-1.5.4-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a254b98dbcc744c723a838c03b74a8a34c0558c9ac5c86d5561703362231107d"},
{file = "scipy-1.5.4-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:368c0f69f93186309e1b4beb8e26d51dd6f5010b79264c0f1e9ca00cd92ea8c9"},
{file = "scipy-1.5.4-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:4598cf03136067000855d6b44d7a1f4f46994164bcd450fb2c3d481afc25dd06"},
{file = "scipy-1.5.4-cp36-cp36m-win32.whl", hash = "sha256:e98d49a5717369d8241d6cf33ecb0ca72deee392414118198a8e5b4c35c56340"},
{file = "scipy-1.5.4-cp36-cp36m-win_amd64.whl", hash = "sha256:65923bc3809524e46fb7eb4d6346552cbb6a1ffc41be748535aa502a2e3d3389"},
{file = "scipy-1.5.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:9ad4fcddcbf5dc67619379782e6aeef41218a79e17979aaed01ed099876c0e62"},
{file = "scipy-1.5.4-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:f87b39f4d69cf7d7529d7b1098cb712033b17ea7714aed831b95628f483fd012"},
{file = "scipy-1.5.4-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:25b241034215247481f53355e05f9e25462682b13bd9191359075682adcd9554"},
{file = "scipy-1.5.4-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:fa789583fc94a7689b45834453fec095245c7e69c58561dc159b5d5277057e4c"},
{file = "scipy-1.5.4-cp37-cp37m-win32.whl", hash = "sha256:d6d25c41a009e3c6b7e757338948d0076ee1dd1770d1c09ec131f11946883c54"},
{file = "scipy-1.5.4-cp37-cp37m-win_amd64.whl", hash = "sha256:2c872de0c69ed20fb1a9b9cf6f77298b04a26f0b8720a5457be08be254366c6e"},
{file = "scipy-1.5.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e360cb2299028d0b0d0f65a5c5e51fc16a335f1603aa2357c25766c8dab56938"},
{file = "scipy-1.5.4-cp38-cp38-manylinux1_i686.whl", hash = "sha256:3397c129b479846d7eaa18f999369a24322d008fac0782e7828fa567358c36ce"},
{file = "scipy-1.5.4-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:168c45c0c32e23f613db7c9e4e780bc61982d71dcd406ead746c7c7c2f2004ce"},
{file = "scipy-1.5.4-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:213bc59191da2f479984ad4ec39406bf949a99aba70e9237b916ce7547b6ef42"},
{file = "scipy-1.5.4-cp38-cp38-win32.whl", hash = "sha256:634568a3018bc16a83cda28d4f7aed0d803dd5618facb36e977e53b2df868443"},
{file = "scipy-1.5.4-cp38-cp38-win_amd64.whl", hash = "sha256:b03c4338d6d3d299e8ca494194c0ae4f611548da59e3c038813f1a43976cb437"},
{file = "scipy-1.5.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3d5db5d815370c28d938cf9b0809dade4acf7aba57eaf7ef733bfedc9b2474c4"},
{file = "scipy-1.5.4-cp39-cp39-manylinux1_i686.whl", hash = "sha256:6b0ceb23560f46dd236a8ad4378fc40bad1783e997604ba845e131d6c680963e"},
{file = "scipy-1.5.4-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:ed572470af2438b526ea574ff8f05e7f39b44ac37f712105e57fc4d53a6fb660"},
{file = "scipy-1.5.4-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:8c8d6ca19c8497344b810b0b0344f8375af5f6bb9c98bd42e33f747417ab3f57"},
{file = "scipy-1.5.4-cp39-cp39-win32.whl", hash = "sha256:d84cadd7d7998433334c99fa55bcba0d8b4aeff0edb123b2a1dfcface538e474"},
{file = "scipy-1.5.4-cp39-cp39-win_amd64.whl", hash = "sha256:cc1f78ebc982cd0602c9a7615d878396bec94908db67d4ecddca864d049112f2"},
{file = "scipy-1.5.4.tar.gz", hash = "sha256:4a453d5e5689de62e5d38edf40af3f17560bfd63c9c5bd228c18c1f99afa155b"},
]
toml = [
{file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
{file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
]
typed-ast = [
{file = "typed_ast-1.4.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:2068531575a125b87a41802130fa7e29f26c09a2833fea68d9a40cf33902eba6"},
{file = "typed_ast-1.4.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:c907f561b1e83e93fad565bac5ba9c22d96a54e7ea0267c708bffe863cbe4075"},
{file = "typed_ast-1.4.3-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:1b3ead4a96c9101bef08f9f7d1217c096f31667617b58de957f690c92378b528"},
{file = "typed_ast-1.4.3-cp35-cp35m-win32.whl", hash = "sha256:dde816ca9dac1d9c01dd504ea5967821606f02e510438120091b84e852367428"},
{file = "typed_ast-1.4.3-cp35-cp35m-win_amd64.whl", hash = "sha256:777a26c84bea6cd934422ac2e3b78863a37017618b6e5c08f92ef69853e765d3"},
{file = "typed_ast-1.4.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f8afcf15cc511ada719a88e013cec87c11aff7b91f019295eb4530f96fe5ef2f"},
{file = "typed_ast-1.4.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:52b1eb8c83f178ab787f3a4283f68258525f8d70f778a2f6dd54d3b5e5fb4341"},
{file = "typed_ast-1.4.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:01ae5f73431d21eead5015997ab41afa53aa1fbe252f9da060be5dad2c730ace"},
{file = "typed_ast-1.4.3-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:c190f0899e9f9f8b6b7863debfb739abcb21a5c054f911ca3596d12b8a4c4c7f"},
{file = "typed_ast-1.4.3-cp36-cp36m-win32.whl", hash = "sha256:398e44cd480f4d2b7ee8d98385ca104e35c81525dd98c519acff1b79bdaac363"},
{file = "typed_ast-1.4.3-cp36-cp36m-win_amd64.whl", hash = "sha256:bff6ad71c81b3bba8fa35f0f1921fb24ff4476235a6e94a26ada2e54370e6da7"},
{file = "typed_ast-1.4.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0fb71b8c643187d7492c1f8352f2c15b4c4af3f6338f21681d3681b3dc31a266"},
{file = "typed_ast-1.4.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:760ad187b1041a154f0e4d0f6aae3e40fdb51d6de16e5c99aedadd9246450e9e"},
{file = "typed_ast-1.4.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5feca99c17af94057417d744607b82dd0a664fd5e4ca98061480fd8b14b18d04"},
{file = "typed_ast-1.4.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:95431a26309a21874005845c21118c83991c63ea800dd44843e42a916aec5899"},
{file = "typed_ast-1.4.3-cp37-cp37m-win32.whl", hash = "sha256:aee0c1256be6c07bd3e1263ff920c325b59849dc95392a05f258bb9b259cf39c"},
{file = "typed_ast-1.4.3-cp37-cp37m-win_amd64.whl", hash = "sha256:9ad2c92ec681e02baf81fdfa056fe0d818645efa9af1f1cd5fd6f1bd2bdfd805"},
{file = "typed_ast-1.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b36b4f3920103a25e1d5d024d155c504080959582b928e91cb608a65c3a49e1a"},
{file = "typed_ast-1.4.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:067a74454df670dcaa4e59349a2e5c81e567d8d65458d480a5b3dfecec08c5ff"},
{file = "typed_ast-1.4.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7538e495704e2ccda9b234b82423a4038f324f3a10c43bc088a1636180f11a41"},
{file = "typed_ast-1.4.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:af3d4a73793725138d6b334d9d247ce7e5f084d96284ed23f22ee626a7b88e39"},
{file = "typed_ast-1.4.3-cp38-cp38-win32.whl", hash = "sha256:f2362f3cb0f3172c42938946dbc5b7843c2a28aec307c49100c8b38764eb6927"},
{file = "typed_ast-1.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:dd4a21253f42b8d2b48410cb31fe501d32f8b9fbeb1f55063ad102fe9c425e40"},
{file = "typed_ast-1.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f328adcfebed9f11301eaedfa48e15bdece9b519fb27e6a8c01aa52a17ec31b3"},
{file = "typed_ast-1.4.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:2c726c276d09fc5c414693a2de063f521052d9ea7c240ce553316f70656c84d4"},
{file = "typed_ast-1.4.3-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:cae53c389825d3b46fb37538441f75d6aecc4174f615d048321b716df2757fb0"},
{file = "typed_ast-1.4.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:b9574c6f03f685070d859e75c7f9eeca02d6933273b5e69572e5ff9d5e3931c3"},
{file = "typed_ast-1.4.3-cp39-cp39-win32.whl", hash = "sha256:209596a4ec71d990d71d5e0d312ac935d86930e6eecff6ccc7007fe54d703808"},
{file = "typed_ast-1.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:9c6d1a54552b5330bc657b7ef0eae25d00ba7ffe85d9ea8ae6540d2197a3788c"},
{file = "typed_ast-1.4.3.tar.gz", hash = "sha256:fb1bbeac803adea29cedd70781399c99138358c26d05fcbd23c13016b7f5ec65"},
]
typing-extensions = [
{file = "typing_extensions-3.10.0.0-py2-none-any.whl", hash = "sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497"},
{file = "typing_extensions-3.10.0.0-py3-none-any.whl", hash = "sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84"},
{file = "typing_extensions-3.10.0.0.tar.gz", hash = "sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342"},
]
zipp = [
{file = "zipp-3.5.0-py3-none-any.whl", hash = "sha256:957cfda87797e389580cb8b9e3870841ca991e2125350677b2ca83a0e99390a3"},
{file = "zipp-3.5.0.tar.gz", hash = "sha256:f5812b1e007e48cff63449a5e9f4e7ebea716b4111f9c4f9a645f91d579bf0c4"},
]

View File

@@ -5,14 +5,32 @@ description = ""
authors = ["Deepak <dmallubhotla+github@gmail.com>"]
[tool.poetry.dependencies]
python = "^3.7"
python = "^3.8,<3.10"
numpy = "^1.21.1"
scipy = "~1.5"
more-itertools = "^8.8.0"
[tool.poetry.dev-dependencies]
pytest = ">=6"
flake8 = "^3.8.4"
pytest-cov = "^2.10.1"
mypy = "^0.790"
mypy = "^0.910"
[build-system]
requires = ["poetry>=0.12"]
build-backend = "poetry.masonry.api"
[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "--junitxml pytest.xml --cov pathfinder --cov-report=xml:coverage.xml --cov-fail-under=90 --cov-report=html"
junit_family = "xunit1"
[tool.mypy]
plugins = "numpy.typing.mypy_plugin"
[[tool.mypy.overrides]]
module = [
"scipy",
"scipy.optimize"
]
ignore_missing_imports = true

3
renovate.json Normal file
View File

@@ -0,0 +1,3 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json"
}

29
scripts/patch.sh Normal file
View File

@@ -0,0 +1,29 @@
#!/usr/bin/env bash
set -Eeuo pipefail
if [ -z "$(git status --porcelain)" ]; then
# Working directory clean
branch_name=$(git symbolic-ref -q HEAD)
branch_name=${branch_name##refs/heads/}
branch_name=${branch_name:-HEAD}
poetry version patch
version=`sed 's/version = "\([0-9]*.[0-9]*.[0-9]*\)"/\1/p' -n <pyproject.toml`
read -p "Create commit for version $version? " -n 1 -r
echo # (optional) move to a new line
if [[ $REPLY =~ ^[Yy]$ ]]
then
# do dangerous stuff
echo "Creating a new patch"
git add pyproject.toml
git commit -m "Created version $version"
git tag -a "$version" -m "patch.sh created version $version"
git push --tags
else
echo "Surrendering, clean up by reverting pyproject.toml..."
exit 2
fi
else
echo "Can't create patch version, working tree unclean..."
exit 1
fi

View File

@@ -0,0 +1,109 @@
import numpy
import pathfinder.model.oscillating
def chunk_n_sort(pts):
pt_length = 7
chunked_pts = [pts[i: i + pt_length] for i in range(0, len(pts), pt_length)]
return chunked_pts
def print_result(msg, result):
print(msg)
print(f"\tResult: {result.pathfinder_x}")
print(f"\tSuccess: {result.success}. {result.message}")
try:
print(f"\tFunc evals: {result.nfev}")
except AttributeError:
pass
try:
print(f"\tJacb evals: {result.njev}")
except AttributeError:
pass
def test_one_dipole_six_dot_two_frequencies():
# setup
dot_inputs = [
([0, 0, .01], 5), ([-1, 0, -.01], 5), ([-2, 0, -.01], 5), ([0, -1, .01], 5), ([-1, -1, 0], 5), ([-2, -1, 0], 5),
([0, 0, .01], 1), ([-1, 0, -.01], 1), ([-2, 0, -.01], 1), ([0, -1, .01], 1), ([-1, -1, 0], 1), ([-2, -1, 0], 1),
]
dipole = pathfinder.model.oscillating.OscillatingDipole([1, 2, 3], [0, 4, -1], 7)
expected_result = numpy.array([1, 2, 3, 0, 4, -1, 7])
dipole_arrangement = pathfinder.model.oscillating.OscillatingDipoleArrangement([dipole])
dot_measurements = dipole_arrangement.get_dot_measurements(dot_inputs)
model = pathfinder.model.oscillating.DotOscillatingDipoleModel(dot_measurements, 1)
res = model.sol()
print_result("one oscillating dipole six dots", res)
assert res.success, "The solution for a single dipole and six dots should have succeeded."
numpy.testing.assert_allclose(res.pathfinder_x, expected_result, err_msg="Dipole wasn't as expected.", rtol=1e-6, atol=1e-6)
def test_two_dipole_six_dot_two_frequencies():
# setup
dot_inputs = [
([0, 0, .01], 5), ([-1, 0, -.01], 5), ([-2, 0, -.01], 5), ([0, -1, .01], 5), ([-1, -1, 0], 5), ([-2, -1, 0], 5),
([0, -2, .01], 5), ([-1, -2, -.01], 5), ([-2, -2, -.01], 5), ([0, -3, .01], 5), ([-1, -3, 0], 5), ([-2, -3, 0], 5),
([0, 0, .01], 1), ([-1, 0, -.01], 1), ([-2, 0, -.01], 1), ([0, -1, .01], 1), ([-1, -1, 0], 1), ([-2, -1, 0], 1),
([0, -2, .01], 1), ([-1, -2, -.01], 1), ([-2, -2, -.01], 1), ([0, -3, .01], 1), ([-1, -3, 0], 1), ([-2, -3, 0], 1),
]
dipole = pathfinder.model.oscillating.OscillatingDipole([1, 2, 3], [0, 4, -1], 7)
dipole2 = pathfinder.model.oscillating.OscillatingDipole([-1, 2, 0], [2, -1, 1], 4)
expected_result = numpy.array([1, -2, 0, 2, -1, 1, 4, 1, 2, 3, 0, 4, -1, 7])
dipole_arrangement = pathfinder.model.oscillating.OscillatingDipoleArrangement([dipole, dipole2])
dot_measurements = dipole_arrangement.get_dot_measurements(dot_inputs)
model = pathfinder.model.oscillating.DotOscillatingDipoleModel(dot_measurements, 2)
res = model.sol()
print_result("two oscillating dipole six dots", res)
assert res.success, "The solution for two dipole and six dots should have succeeded."
numpy.testing.assert_allclose(res.pathfinder_x, expected_result, err_msg="Dipole wasn't as expected.", rtol=1e-6, atol=1e-6)
def test_two_dipole_six_dot_two_frequencies_morerealistic():
# setup
dot_inputs = [
([0, 0, .01], 5), ([-1, 0, -.01], 5), ([-2, 0, -.01], 5), ([-3, 0, 0], 5), ([0, -1, .01], 5), ([-1, -1, 0], 5), ([-2, -1, 0], 5), ([-3, -1, 0], 5),
([0, -2, .01], 5), ([-1, -2, -.01], 5), ([-2, -2, -.01], 5), ([-3, -2, 0], 5), ([0, -3, .01], 5), ([-1, -3, 0], 5), ([-2, -3, 0], 5), ([-3, -3, 0], 5),
([0, 0, .01], 1), ([-1, 0, -.01], 1), ([-2, 0, -.01], 1), ([-3, 0, 0], 1), ([0, -1, .01], 1), ([-1, -1, 0], 1), ([-2, -1, 0], 1), ([-3, -1, 0], 1),
([0, -2, .01], 1), ([-1, -2, -.01], 1), ([-2, -2, -.01], 1), ([-3, -2, 0], 1), ([0, -3, .01], 1), ([-1, -3, 0], 1), ([-2, -3, 0], 1), ([-3, -3, 0], 1),
]
dipole = pathfinder.model.oscillating.OscillatingDipole([1, 2, 3], [0, 4, -1], 7)
dipole2 = pathfinder.model.oscillating.OscillatingDipole([-1, 2, 0], [-1, 2, 1], 4)
expected_result = numpy.array([1, -2, 0, -1, 2, 1, 4, 1, 2, 3, 0, 4, -1, 7])
dipole_arrangement = pathfinder.model.oscillating.OscillatingDipoleArrangement([dipole, dipole2])
dot_measurements = dipole_arrangement.get_dot_measurements(dot_inputs)
model = pathfinder.model.oscillating.DotOscillatingDipoleModel(dot_measurements, 2)
res = model.sol()
print_result("two oscillating dipole six dots", res)
numpy.testing.assert_allclose(res.pathfinder_x, expected_result, err_msg="Dipole wasn't as expected.", rtol=1e-6, atol=1e-6)
def test_two_dipole_eighteen_dot_two_frequencies_morerealistic():
# setup
dot_inputs = [
([0, 0, .01], 5), ([-1, 0, -.01], 5), ([-2, 0, -.01], 5), ([0, -1, .01], 5), ([-1, -1, 0], 5), ([-2, -1, 0], 5),
([0, -2, .01], 5), ([-1, -2, -.01], 5), ([-2, -2, -.01], 5), ([0, -3, .01], 5), ([-1, -3, 0], 5), ([-2, -3, 0], 5),
([0, -4, .01], 5), ([-1, -4, -.01], 5), ([-2, -4, -.01], 5), ([0, -5, .01], 5), ([-1, -5, 0], 5), ([-2, -5, 0], 5),
([0, 0, .01], 1), ([-1, 0, -.01], 1), ([-2, 0, -.01], 1), ([0, -1, .01], 1), ([-1, -1, 0], 1), ([-2, -1, 0], 1),
([0, -2, .01], 1), ([-1, -2, -.01], 1), ([-2, -2, -.01], 1), ([0, -3, .01], 1), ([-1, -3, 0], 1), ([-2, -3, 0], 1),
([0, -4, .01], 1), ([-1, -4, -.01], 1), ([-2, -4, -.01], 1), ([0, -5, .01], 1), ([-1, -5, 0], 1), ([-2, -5, 0], 1),
]
dipole = pathfinder.model.oscillating.OscillatingDipole([1, 2, 3], [0, 4, -1], 7)
dipole2 = pathfinder.model.oscillating.OscillatingDipole([-1, 2, 0], [-1, 2, 1], 4)
expected_result = numpy.array([1, -2, 0, -1, 2, 1, 4, 1, 2, 3, 0, 4, -1, 7])
dipole_arrangement = pathfinder.model.oscillating.OscillatingDipoleArrangement([dipole, dipole2])
dot_measurements = dipole_arrangement.get_dot_measurements(dot_inputs)
model = pathfinder.model.oscillating.DotOscillatingDipoleModel(dot_measurements, 2)
res = model.sol()
print_result("two oscillating dipole six dots", res)
assert res.success, "The solution for two dipole and six dots should have succeeded."
numpy.testing.assert_allclose(res.pathfinder_x, expected_result, err_msg="Dipole wasn't as expected.", rtol=1e-6, atol=1e-6)

View File

@@ -0,0 +1,43 @@
import numpy
import numpy.testing
import pathfinder.model.oscillating as model
def test_dot_v_from_dipole():
dot_position1 = (-1, -1, -1)
dot_frequency1 = 11
expected_v1 = 0.00001421963287022476
dot = model.DotMeasurement(expected_v1, dot_position1, dot_frequency1)
# and dipole located at (4, 5, 6) with p=(1, 2, 3) and w = 7
pt = numpy.array((1, 2, 3, 4, 5, 6, 7))
numpy.testing.assert_allclose(dot.v_for_point(pt), dot.v, err_msg="v from dipole at a dot was incorrect!")
numpy.testing.assert_allclose(dot.cost(pt), 0, atol=1e-12, err_msg="cost should be zero")
def test_jac():
expected_jac = [
3.742008650059147e-6, 4.4904103800709765e-6, 5.238812110082806e-6,
-3.12967996186765e-6, -3.1568945702317167e-6, -3.184109178595783e-6,
8.603475350051953e-7
]
expected_jac2 = [
-4.428720903021825e-6, 3.5429767224174605e-6, 4.428720903021825e-6,
-6.804125751006259e-6, -4.0261099118380183e-7, 2.3754048479844336e-6,
5.181603456535536e-6
]
dot = model.DotMeasurement(50, (-1, -1, -1), 11)
# dipole located at (4, 5, 6) with p=(1, 2, 3) and w = 7
pt = numpy.array((1, 2, 3, 4, 5, 6, 7))
pt2 = numpy.array((2, 5, 3, 4, -5, -6, 2))
pts = numpy.append(pt, pt2)
expected_jac_all = expected_jac + expected_jac2
assert len(dot.jac_pt(pt)) == 7
numpy.testing.assert_allclose(dot.jac_pt(pt), expected_jac, err_msg="Jac pt doesn't match Mathematica result.")
numpy.testing.assert_allclose(dot.jac(pts), expected_jac_all, err_msg="whole row should match")

View File

@@ -0,0 +1,22 @@
import numpy
import pathfinder.model.oscillating as model
def test_static_dipole():
d1 = model.OscillatingDipole((1, 2, 3), (4, 5, 6), 7)
d2 = model.OscillatingDipole((2, 5, 3), (4, -5, -6), 2)
dipoles = model.OscillatingDipoleArrangement([d1, d2])
dot_position1 = (-1, -1, -1)
dot_frequency1 = 11
expected_v1 = 0.00001421963287022476
expected_v2 = 0.00001107180225755457
numpy.testing.assert_allclose(d1.s_at_position(dot_position1, dot_frequency1), expected_v1, err_msg="Voltage at dot isn't as expected.")
dot_measurements = dipoles.get_dot_measurements([(dot_position1, dot_frequency1)])
assert len(dot_measurements) == 1, "Should have only had one dot measurement."
measurement = dot_measurements[0]
numpy.testing.assert_allclose(measurement.r, dot_position1, err_msg="Dot position should have been passed over")
numpy.testing.assert_allclose(measurement.f, dot_frequency1, err_msg="Dot frequency should have been passed over")
numpy.testing.assert_allclose(measurement.v, expected_v1 + expected_v2, err_msg="Voltage at dot isn't as expected.")

View File

@@ -0,0 +1,35 @@
import numpy
import pathfinder.model.oscillating as model
def test_dotdipolemodel_repr():
mod = model.DotOscillatingDipoleModel((), 1)
assert repr(mod) == "DotOscillatingDipoleModel([], 1)"
def test_dotdipolemodel_m():
mod = model.DotOscillatingDipoleModel([model.DotMeasurement(1, (-1, -1, -1), 11), model.DotMeasurement(2, (0, 0, 0), 3)], 1)
assert mod.m == 2
def test_dotdipolemodel_cost():
mod = model.DotOscillatingDipoleModel([model.DotMeasurement(.05, (-1, -1, -1), 11), model.DotMeasurement(.002, (0, 0, 0), 3)], 1)
costs = mod.costs()
jac = mod.jac()
pt_to_test = numpy.array((1, 2, 3, 4, 5, 6, 7))
expected_cost = [-0.04998578036712978, -0.00191383161468913]
expected_jacobian = [
[
3.742008650059147e-6, 4.4904103800709765e-6, 5.238812110082806e-6,
-3.12967996186765e-6, -3.1568945702317167e-6, -3.184109178595783e-6,
8.603475350051953e-7
], [
0.000021542096327717702, 0.000026927620409647127, 0.000032313144491576555,
-0.000021472154456523818, -0.0000228010500092077, -0.000024129945561891588,
-8.489496089740964e-6
]
]
numpy.testing.assert_allclose(costs(pt_to_test), expected_cost, err_msg="Costs aren't as expected.")
numpy.testing.assert_allclose(jac(pt_to_test), expected_jacobian, err_msg="Jacobian aren't as expected.")

View File

@@ -0,0 +1,20 @@
import pathfinder.model.oscillating.util as util
import numpy
def test_util_flip():
pt = numpy.array((1, 2, 3, 4, 5, 6, 7))
expected = (1, 2, 3, 4, 5, 6, 7)
numpy.testing.assert_allclose(util.flip_chunk_to_positive_px(pt), expected, err_msg="Point should remain unchanged.")
def test_util_flip_negative():
pt = numpy.array((-1, -2, 3, 4, 5, 6, 7))
expected = (1, 2, -3, 4, 5, 6, 7)
numpy.testing.assert_allclose(util.flip_chunk_to_positive_px(pt), expected, err_msg="Point should remain unchanged.")
def test_util_normalise_two():
pt = numpy.array((1, 2, 3, 4, 5, 6, 7, -.5, 1, 1.5, 2, 2.5, 3, 3.5))
expected = (.5, -1, -1.5, 2, 2.5, 3, 3.5, 1, 2, 3, 4, 5, 6, 7)
numpy.testing.assert_allclose(util.normalize_oscillating_dipole_list(pt), expected, err_msg="Sort should have happened.")

View File

@@ -0,0 +1,45 @@
import numpy
import numpy.testing
import pathfinder.model.static as model
def test_dot():
dot = model.DotMeasurement(0.235, (1, 2, 3))
assert dot.v == 0.235
numpy.testing.assert_array_equal(dot.r, (1, 2, 3), "These arrays should have been equal!")
def test_dot_v_from_dipole():
# for a dot located at (1, 2, 3)
dot = model.DotMeasurement(50, (1, 2, 3))
# and dipole located at (4, 7, 11) with p=(8, 9, 10)
pt = numpy.array((8, 9, 10, 4, 7, 11))
# V should be -0.153584
target = -0.1535844174880402
cost = -50.1535844174880402
numpy.testing.assert_allclose(dot.v_for_point(pt), target, err_msg="v from dipole at a dot was incorrect!")
numpy.testing.assert_allclose(dot.cost(pt), cost, err_msg="cost from dipole at a dot was incorrect!")
def test_dot_jac():
# for a dot located at (1, 2, 3)
dot = model.DotMeasurement(50, (1, 2, 3))
# and dipole located at (4, 7, 11) with p=(8, 9, 10)
pt = numpy.array((8, 9, 10, 4, 7, 11))
target_jac_pt = [-0.003092303707812889, -0.005153839513021483, -0.00824614322083437, 0.0058585481811285, 0.01423090787983279, 0.02730483137919137]
# assume a second dipole at (12, 13, -5), with p = (-1, -2, -3)
second_target = [-0.002054993939616119, -0.002054993939616119, 0.001494541046993541, 5.494636202182148e-6, 0.0001923122670763748, 0.0006923241614749492]
pt2 = numpy.array((-1, -2, -3, 12, 13, -5))
jac_row_target = target_jac_pt + second_target
pts = numpy.append(pt, pt2)
assert len(dot.jac_pt(pt)) == 6
numpy.testing.assert_allclose(dot.jac_pt(pt), target_jac_pt, err_msg="Jac pt doesn't match Mathematica result.")
numpy.testing.assert_allclose(dot.jac(pts), jac_row_target, err_msg="whole row should match")

View File

@@ -0,0 +1,76 @@
import numpy
import pathfinder.model.static as model
def test_dotdipolemodel_repr():
mod = model.DotDipoleModel((), 1)
assert repr(mod) == "DotDipoleModel([], 1)"
def test_dotdipolemodel_m():
mod = model.DotDipoleModel([model.DotMeasurement(1, (0, 0, 0)), model.DotMeasurement(2, (0, 0, 0))], 1)
assert mod.m == 2
def test_dotdipolemodel_cost():
mod = model.DotDipoleModel([model.DotMeasurement(1, (0, 0, 1)), model.DotMeasurement(2, (1, 0, 0))], 1)
costs = mod.costs()
jac = mod.jac()
pt_to_test = numpy.array((1, 2, 3, 4, 5, 6))
expected_cost = [-1.05408565512728256, -2.05293155269909457]
expected_jacobian = [
[
-0.007460090362383803, -0.009325112952979752, -0.009325112952979752,
0.007968732887091788, 0.00856214916591777, 0.006697126575321822
], [
-0.00512240832571883, -0.008537347209531383, -0.01024481665143766,
0.005098015905120168, 0.007927536694564856, 0.008488562368334061
]
]
numpy.testing.assert_allclose(costs(pt_to_test), expected_cost, err_msg="Costs aren't as expected.")
numpy.testing.assert_allclose(jac(pt_to_test), expected_jacobian, err_msg="Jacobian aren't as expected.")
def print_result(msg, result):
print(msg)
print(f"\tResult: {result.x}")
print(f"\tSuccess: {result.success}. {result.message}")
try:
print(f"\tFunc evals: {result.nfev}")
except AttributeError:
pass
try:
print(f"\tJacb evals: {result.njev}")
except AttributeError:
pass
def test_dot_dipole_model_solution():
v1 = -0.05547767706400186526225414
v2 = -0.06018573388098888319642888
v3 = -0.06364032191901859480476888
v4 = -0.06488383879243851188402150
v5 = -0.06297148063759813929659130
v6 = -0.05735489606460216
v7 = -0.07237320672886623
v8 = -0.1082531754730548
v9 = -0.04471694936155558
c1 = model.DotMeasurement(v1, [0, 0, 1])
c2 = model.DotMeasurement(v2, [0, 0, 2])
c3 = model.DotMeasurement(v3, [0, 0, 3])
c4 = model.DotMeasurement(v4, [0, 0, 4])
c5 = model.DotMeasurement(v5, [0, 0, 5])
c6 = model.DotMeasurement(v6, [0, 0, 6])
c7 = model.DotMeasurement(v7, [1, 1, 7])
c8 = model.DotMeasurement(v8, [1, 2, 3])
c9 = model.DotMeasurement(v9, [0, -1, 0])
expected_result = numpy.array([1, 3, 5, 5, 6, 7])
mod = model.DotDipoleModel([c1, c2, c3, c4, c5, c6, c7, c8, c9], 1)
res = mod.sol()
assert res.success, "The solution for a single dipole should have succeeded."
numpy.testing.assert_allclose(res.x, expected_result, err_msg="Dipole wasn't as expected.")

View File

@@ -0,0 +1,120 @@
import numpy
import pathfinder.model.static
import pytest
def chunk_n_sort(pts):
pt_length = 6
chunked_pts = [pts[i: i + pt_length] for i in range(0, len(pts), pt_length)]
return sorted(chunked_pts, key=lambda x: x[0])
def print_result(msg, result):
print(msg)
print(f"\tResult: {chunk_n_sort(result.x)}")
print(f"\tSuccess: {result.success}. {result.message}")
try:
print(f"\tFunc evals: {result.nfev}")
except AttributeError:
pass
try:
print(f"\tJacb evals: {result.njev}")
except AttributeError:
pass
def test_one_dipole_six_dot():
# setup
dot_positions = [[0, 0, 0], [-1, 0, 0], [-2, 0, 0], [-1, -1, 0], [-1, -1, 1], [0, -2, 0]]
dipole = pathfinder.model.static.StaticDipole([1, 2, 3], [0, 4, 0])
expected_result = numpy.array([1, 2, 3, 0, 4, 0])
dipole_arrangement = pathfinder.model.static.StaticDipoleArrangement([dipole])
dot_measurements = dipole_arrangement.get_dot_measurements(dot_positions)
model = pathfinder.model.static.DotDipoleModel(dot_measurements, 1)
res = model.sol()
print_result("one dipole six dots", res)
assert res.success, "The solution for a single dipole and six dots should have succeeded."
numpy.testing.assert_allclose(res.x, expected_result, err_msg="Dipole wasn't as expected.", rtol=1e-6, atol=1e-6)
@pytest.mark.skip(reason="don't care at the moment")
def test_two_dipole_thirteen_dot():
dot_positions = [[x, y, 0] for x in (-4, -1, 1, 4) for y in (-1, -5, -9)]
dot_positions.append((1, -1, 2))
# dot_positions.append((-1, -1, -2))
dipole = pathfinder.model.static.StaticDipole([0, 8, 0], [0, 5, 0])
dipole2 = pathfinder.model.static.StaticDipole([8, 1, 1], [2, 2, -2])
expected_result = numpy.array([0, 8, 0, 0, 5, 0, 8, 1, 1, 2, 2, -2])
dipole_arrangement = pathfinder.model.static.StaticDipoleArrangement([dipole, dipole2])
dot_measurements = dipole_arrangement.get_dot_measurements(dot_positions)
model = pathfinder.model.static.DotDipoleModel(dot_measurements, 2)
res = model.sol(initial_dipole=(2, 2, 0), initial_position=(0, 3, 0))
print(model.costs()(res.x))
print(res.x)
print_result("two dipole 13 dot", res)
assert res.success, "The solution for two dipoles and twelve dots should have succeeded."
numpy.testing.assert_allclose(chunk_n_sort(res.x), chunk_n_sort(expected_result), err_msg="Dipole wasn't as expected.", rtol=1e-6, atol=1e-4)
@pytest.mark.skip(reason="don't care at the moment")
def test_two_dipole_twelve_dot():
dot_positions = [[x, y, 0] for x in (-4, -1, 1, 4) for y in (-1, -5, -9)][:-1]
dot_positions.append((1, -1, 2))
# dot_positions.append((-1, -1, -2))
dipole = pathfinder.model.static.StaticDipole([0, 8, 0], [0, 5, 0])
dipole2 = pathfinder.model.static.StaticDipole([8, 1, 1], [2, 2, -2])
expected_result = numpy.array([0, 8, 0, 0, 5, 0, 8, 1, 1, 2, 2, -2])
dipole_arrangement = pathfinder.model.static.StaticDipoleArrangement([dipole, dipole2])
dot_measurements = dipole_arrangement.get_dot_measurements(dot_positions)
model = pathfinder.model.static.DotDipoleModel(dot_measurements, 2)
res = model.sol(initial_dipole=(2, 2, 0), initial_position=(0, 3, 0), use_root=False)
print(model.costs()(res.x))
print(res.x)
print_result("two dipole 13 dot", res)
assert res.success, "The solution for two dipoles and twelve dots should have succeeded."
numpy.testing.assert_allclose(chunk_n_sort(res.x), chunk_n_sort(expected_result), err_msg="Dipole wasn't as expected.", rtol=1e-6, atol=1e-4)
@pytest.mark.skip(reason="don't care at the moment")
def test_two_dipole_twelve_dot_smallguy():
dot_positions = [[x, y, 0] for x in (-4, -1, 1, 4) for y in (-1, -5, -9)][:-1]
dot_positions.append((1, -1, 2))
# dot_positions.append((-1, -1, -2))
dipole = pathfinder.model.static.StaticDipole([0, 1, 0], [0, 5, 0])
dipole2 = pathfinder.model.static.StaticDipole([8, 1, 1], [2, 2, -2])
expected_result = numpy.array([0, 1, 0, 0, 5, 0, 8, 1, 1, 2, 2, -2])
dipole_arrangement = pathfinder.model.static.StaticDipoleArrangement([dipole, dipole2])
dot_measurements = dipole_arrangement.get_dot_measurements(dot_positions)
model = pathfinder.model.static.DotDipoleModel(dot_measurements, 2)
res = model.sol(initial_dipole=(2, 2, 0), initial_position=(0, 3, 0), use_root=False)
print(model.costs()(res.x))
print(res.x)
print_result("two dipole 13 dot", res)
assert res.success, "The solution for two dipoles and twelve dots should have succeeded."
numpy.testing.assert_allclose(chunk_n_sort(res.x), chunk_n_sort(expected_result), err_msg="Dipole wasn't as expected.", rtol=1e-6, atol=1e-4)
def test_two_dipole_twelve_dot_2():
dot_positions = [[x, y, 0] for x in (-4, -1, 1, 4) for y in (-1, -5, -9)]
dot_positions.append((1, -1, 2))
dot_positions.append((-1, -1, -2))
dipole = pathfinder.model.static.StaticDipole([0, 8, 0], [0, 5, 0])
dipole2 = pathfinder.model.static.StaticDipole([8, 1, 1], [2, 2, -2])
expected_result = numpy.array([0, 8, 0, 0, 5, 0, 8, 1, 1, 2, 2, -2])
dipole_arrangement = pathfinder.model.static.StaticDipoleArrangement([dipole, dipole2])
dot_measurements = dipole_arrangement.get_dot_measurements(dot_positions)
model = pathfinder.model.static.DotDipoleModel(dot_measurements, 2)
res = model.sol()
print(model.costs()(res.x))
print_result("two dipole twelve dot", res)
# assert res.success, "The solution for two dipoles and twelve dots should have succeeded."
numpy.testing.assert_allclose(chunk_n_sort(res.x), chunk_n_sort(expected_result), err_msg="Dipole wasn't as expected.", rtol=1e-6, atol=1e-4)

View File

@@ -0,0 +1,23 @@
import numpy
import pathfinder.model.static as model
def test_static_dipole():
d1 = model.StaticDipole((1, 2, 3), (4, 5, 6))
d2 = model.StaticDipole((4, 5, 6), (1, 2, 3))
dipoles = model.StaticDipoleArrangement([d1, d2])
dot_position1 = (-1, -1, -1)
expected_v1 = -0.3338923121348659
numpy.testing.assert_allclose(dipoles.get_dot_measurement(dot_position1).v, expected_v1, err_msg="Voltage at dot isn't as expected.")
numpy.testing.assert_allclose(dipoles.get_dot_measurement(dot_position1).r, dot_position1, err_msg="Dot isn't where expected.")
# test multiple dots
dot_position2 = (-1, -1, -5)
expected_v2 = -0.1254445237566694
both_measurements = dipoles.get_dot_measurements([dot_position1, dot_position2])
measured_voltages = numpy.sort([m.v for m in both_measurements])
expected_measured_voltages = numpy.sort([expected_v1, expected_v2])
numpy.testing.assert_allclose(measured_voltages, expected_measured_voltages, err_msg="Didn't get the measured voltages expected")

View File

@@ -1,5 +1,7 @@
from pathfinder import __version__
import pathfinder
def test_version():
assert __version__ == '0.0.1'
assert pathfinder.get_version() == __version__