Compare commits

...
This repository has been archived on 2022-02-23. You can view files and clone it, but cannot push or open issues or pull requests.

16 Commits

Author SHA1 Message Date
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
24 changed files with 794 additions and 210 deletions

View File

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

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

@ -2,8 +2,6 @@ from dataclasses import dataclass
import numpy
import numpy.typing
from pathfinder.model.staticdipole import StaticDipole
@dataclass
class DotMeasurement():
@ -38,9 +36,8 @@ class DotMeasurement():
p = pt[0:3] # hardcoded here because chances
s = pt[3:6] # are we'll only ever work in 3d.
dipole = StaticDipole(p, s)
return dipole.v_at_position(self.r)
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.

View File

@ -1,7 +1,7 @@
from typing import Callable, Sequence
import numpy
from pathfinder.model.dot import DotMeasurement
import scipy.optimize
from pathfinder.model.static.dot import DotMeasurement
class DotDipoleModel():
@ -35,3 +35,10 @@ class DotDipoleModel():
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]

View File

@ -1,31 +0,0 @@
from dataclasses import dataclass
import numpy
import numpy.typing
@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.typing.ArrayLike) -> float:
diff = r - self.s
return self.p.dot(diff) / (numpy.linalg.norm(diff)**3)

84
poetry.lock generated
View File

@ -78,7 +78,7 @@ python-versions = ">=3.5"
[[package]]
name = "mypy"
version = "0.790"
version = "0.910"
description = "Optional static typing for Python"
category = "dev"
optional = false
@ -86,11 +86,12 @@ python-versions = ">=3.5"
[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]]
name = "mypy-extensions"
@ -218,14 +219,6 @@ category = "dev"
optional = false
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
[[package]]
name = "typed-ast"
version = "1.4.3"
description = "a fork of Python 2 and 3 ast modules with type comment support"
category = "dev"
optional = false
python-versions = "*"
[[package]]
name = "typing-extensions"
version = "3.10.0.0"
@ -237,7 +230,7 @@ python-versions = "*"
[metadata]
lock-version = "1.1"
python-versions = "^3.8,<3.10"
content-hash = "223211dbc0d0b43607b649f98a88b1d7c2f07c9d7574508bd8f68f36787966b3"
content-hash = "daffa6e91d6921845be603d5c0565d1afb511a57bb9a2f4b9c1084754b7a5314"
[metadata.files]
atomicwrites = [
@ -323,20 +316,29 @@ more-itertools = [
{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"},
@ -435,38 +437,6 @@ 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"},

View File

@ -14,7 +14,7 @@ more-itertools = "^8.8.0"
pytest = ">=6"
flake8 = "^3.8.4"
pytest-cov = "^2.10.1"
mypy = "^0.790"
mypy = "^0.910"
[build-system]
requires = ["poetry>=0.12"]
@ -22,8 +22,15 @@ 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"
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

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

@ -1,7 +1,7 @@
import numpy
import numpy.testing
import pathfinder.model as model
import pathfinder.model.static as model
def test_dot():

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,107 +0,0 @@
import numpy
import pathfinder.model as model
import scipy.optimize
import pytest
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 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
@pytest.mark.skip(reason="old")
def test_dot_dipole_model_jac():
v1 = -0.05547767706400186526225414
v2 = -0.06018573388098888319642888
v3 = -0.06364032191901859480476888
v4 = -0.06488383879243851188402150
v5 = -0.06297148063759813929659130
v6 = -0.05735489606460216
v7 = -0.07237320672886623
v8 = -0.1082531754730548
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])
mod = model.DotDipoleModel([c1, c2, c3, c4, c5, c6], 1)
res = scipy.optimize.least_squares(mod.costs(), numpy.array([1, 2, 3, 4, 5, 6]), jac=mod.jac(), ftol=1e-12)
print_result("6 dots, least sq", res)
mod2 = model.DotDipoleModel([c1, c2, c3, c4, c5, c6, c7, c8], 1)
res2 = scipy.optimize.least_squares(mod2.costs(), numpy.array([0, 0, 0, 0, 0, 0]), jac=mod2.jac(), ftol=1e-12, gtol=3e-16)
print_result("7 dots, least squares", res2)
print(mod2.costs()(res2.x))
print(mod2.costs()(numpy.array([1, 3, 5, 5, 6, 7])))
@pytest.mark.skip(reason="bad test")
def test_dot_2dipoles_model_jac():
dots_12andone = [
model.DotMeasurement(-0.1978319326584865, [-4, -1, 0]),
model.DotMeasurement(-0.1273171638293727, [-4, -5, 0]),
model.DotMeasurement(-0.05545025617224288, [-4, -9, 0]),
model.DotMeasurement(-0.4960209997369774, [-1, -1, 0]),
model.DotMeasurement(-0.1763373289754278, [-1, -5, 0]),
model.DotMeasurement(-0.04946346672578462, [-1, -9, 0]),
model.DotMeasurement(-0.5633156098386561, [1, -1, 0]),
model.DotMeasurement(-0.113765134433174, [1, -5, 0]),
model.DotMeasurement(-0.0294893572499722, [1, -9, 0]),
model.DotMeasurement(0.7794941616360612, [4, -1, 0]),
model.DotMeasurement(0.1110683086477768, [4, -5, 0]),
model.DotMeasurement(0.01183272220840589, [4, -9, 0]),
model.DotMeasurement(-0.1096485462119833, [1, 1, 1]),
model.DotMeasurement(-0.3925851888077783, [1, 1, -1])
]
# dots = [
# model.Dot(-0.1978319326584865, [-4, -1, 0]),
# model.Dot(-0.1273171638293727, [-4, -5, 0]),
# model.Dot(-0.05545025617224288, [-4, -9, 0]),
# model.Dot(-0.4960209997369774, [-1, -1, 0]),
# model.Dot(-0.1763373289754278, [-1, -5, 0]),
# model.Dot(-0.04946346672578462, [-1, -9, 0]),
# model.Dot(-0.5633156098386561, [1, -1, 0]),
# model.Dot(-0.113765134433174, [1, -5, 0]),
# model.Dot(-0.0294893572499722, [1, -9, 0]),
# model.Dot(0.7794941616360612, [4, -1, 0]),
# model.Dot(0.1110683086477768, [4, -5, 0]),
# model.Dot(0.01183272220840589, [4, -9, 0]),
# model.Dot(-0.0261092728062841, [-4, -13, 0]),
# model.Dot(-0.02035559593904894, [-1, -13, 0]),
# model.Dot(-0.01296894715810522, [1, -13, 0]),
# model.Dot(0.0003306001626435171, [4, -13, 0]),
# model.Dot(0.2612817068810759, [7, -1, 0]),
# model.Dot(0.1203841445911355, [7, -5, 0]),
# model.Dot(0.03425933872931543, [7, -9, 0]),
# model.Dot(0.01068547688208644, [7, -13, 0]),
# ]
mod = model.DotDipoleModel(dots_12andone, 2)
res = scipy.optimize.least_squares(mod.costs(), numpy.array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), jac=mod.jac(), ftol=1e-12)
print_result("6 dots, least sq", res)
print(mod.costs()(res.x))
for val in res.x:
print(val)

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__