Skip to content

Commit 0b1be1b

Browse files
Some phase tweaks... kind of work in progress.
1 parent f9dfed9 commit 0b1be1b

File tree

7 files changed

+106
-7
lines changed

7 files changed

+106
-7
lines changed

siffpy/core/siffreader.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1946,6 +1946,7 @@ def register(
19461946
kwargs.pop('nowarn')
19471947
else:
19481948
warnings.warn("\n\n \t Don't forget to fix the zplane alignment!!")
1949+
19491950
if not self.opened:
19501951
raise RuntimeError("No open .siff or .tiff")
19511952

siffpy/core/utils/circle_fcns.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Functions for circularizing floats and ints
22
from typing import Any, Tuple
3+
import warnings
34

45
import numpy as np
56

@@ -180,6 +181,11 @@ def circ_corr(
180181
>>> 0.9131513426682732
181182
```
182183
"""
184+
if (x.dtype == np.complex128) or (y.dtype == np.complex128):
185+
warnings.warn(
186+
"Passed complex-valued array to `circ_corr`. May be a mistake. \
187+
Consider using `circ_corr_complex` instead."
188+
)
183189
expd_1 = np.exp(1j*x)
184190
expd_2 = np.exp(1j*y)
185191

siffpy/siffmath/flim/traces.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ def subtract_noise(
193193
presumed to be the same as the `FlimTrace`.
194194
"""
195195

196-
# Subtracts a known amount of noise.
196+
# Subtracts the noise if the value is already known.
197197
if n_photons is not None:
198198
if (
199199
isinstance(n_photons, np.ndarray)
@@ -229,6 +229,7 @@ def subtract_noise(
229229
# FLIMParams object.
230230
if self.FLIMParams is None:
231231
return
232+
232233
# Uses the value of the noise, assumes a uniform distribution,
233234
# and subtracts that proportion of photons from the intensity
234235
# and lifetime data.

siffpy/siffmath/phase/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ def phase_shift(x : np.ndarray, shift : PhaseTraceLike)->np.ndarray:
8888
idx = np.angle(np.exp(1j*shift[t]))*n_cols/(2*np.pi)
8989
whole = idx.astype(int)
9090
frac = idx - whole
91-
shifted[:,t] = np.roll(np.abs(1-frac)*x[:,t], whole)
91+
shifted[:,t] = np.roll((1-np.abs(frac))*x[:,t], whole)
9292
shifted[:,t] += np.roll(np.abs(frac)*x[:,t], int(whole+np.sign(frac)))
9393

9494
return shifted

siffpy/siffmath/phase/correlate.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import numpy as np
2+
from typing import Iterable
3+
4+
from siffpy.core.utils.circle_fcns import circ_corr_complex, running_circ_corr_complex
5+
from .traces import PhaseTrace
6+
7+
def circular_correlate(x : PhaseTrace, y : PhaseTrace, method : str = 'Fisher', ) -> np.ndarray:
8+
"""
9+
Circular correlation of two phase traces -- does NOT take error functions into
10+
account. TODO: use the magnitude rather than just the angle for a more robust
11+
estimate of error.
12+
13+
## Arguments
14+
15+
x : PhaseTrace
16+
The first phase trace to correlate.
17+
18+
y : PhaseTrace
19+
The second phase trace to correlate.
20+
21+
method : str
22+
23+
The method to use for the correlation. Options are:
24+
- 'Fisher'
25+
- 'Jammalamadaka
26+
27+
## Returns
28+
29+
np.ndarray
30+
The circular correlation of the two phase traces. A single number.
31+
"""
32+
33+
return circ_corr_complex(
34+
np.exp(1j*x.angle),
35+
np.exp(1j*y.angle),
36+
method = method
37+
)
38+
39+
def correlate(x : PhaseTrace, y : PhaseTrace, dts : Iterable, method : str = 'Fisher', mode : str = 'valid') -> np.ndarray:
40+
"""
41+
Compute the circular cross-correlation of two phase traces, the correlation of `x` with a time-shifted
42+
version of `y`. Does NOT take error functions or the magnitude of the phase into account.
43+
44+
## Arguments
45+
46+
x : PhaseTrace
47+
The first phase trace to correlate.
48+
49+
y : PhaseTrace
50+
The second phase trace to correlate.
51+
52+
method : str
53+
54+
The method to use for the correlation. Options are:
55+
- 'Fisher'
56+
- 'Jammalamadaka
57+
58+
mode : str
59+
60+
The mode to use for the correlation. Options are:
61+
- 'full'
62+
- 'valid'
63+
- 'same'
64+
65+
## Returns
66+
67+
np.ndarray
68+
The cross-circular-correlation of the two phase traces.
69+
"""
70+
if not mode == 'valid':
71+
raise NotImplementedError("Only 'valid' mode is currently supported")
72+
73+
x_angle = np.exp(1j*x.angle)
74+
y_angle = np.exp(1j*y.angle)
75+
76+
raise NotImplementedError("This function is not yet implemented")

siffpy/siffmath/phase/phase_estimates.py

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
an argument vector_timeseries, which is a numpy array,
66
and accept a keyword argument error_estimate, which is a boolean
77
"""
8-
from typing import Callable, Union, Optional, Tuple
8+
from typing import Callable, Union, Optional, Tuple, Any
99
from enum import Enum
1010

1111
import numpy as np
@@ -22,7 +22,8 @@
2222
)
2323

2424
__all__ = [
25-
'pva'
25+
'pva',
26+
'pva_flim',
2627
]
2728

2829
class PhaseErrorFunction(Enum):
@@ -101,6 +102,7 @@ def pva(
101102
time : Optional[np.ndarray] = None,
102103
error_function : Optional[Union[Callable,str]] = 'relative_magnitude',
103104
filter_fcn : Optional[Union[Callable,str]] = None,
105+
angle_coords : Optional[np.ndarray[Any, Any]] = None,
104106
**kwargs
105107
) -> PhaseTrace:
106108
"""
@@ -136,6 +138,15 @@ def pva(
136138
filter_fcn : function
137139
138140
A function to apply to the time series before computing the PVA.
141+
142+
angle_coords : np.ndarray
143+
144+
The angular coordinates of the first dimension of the vector timeseries,
145+
i.e. the angle that each vector component corresponds to. If not provided,
146+
will assume that the angle is linearly spaced between -pi and pi. Can
147+
provide complex numbers on the unit circle or floats corresponding to an
148+
angle in radians.
149+
139150
"""
140151

141152
if normalized:
@@ -144,8 +155,7 @@ def pva(
144155
max_val = sorted_vals[:,int(sorted_vals.shape[-1]*(1.0-1.0/20))]
145156
vector_timeseries = ((vector_timeseries.T - min_val)/(max_val - min_val)).T
146157

147-
angle_coords = np.exp(np.linspace(np.pi, -np.pi, vector_timeseries.shape[0])*1j) # it goes clockwise.
148-
if isinstance(vector_timeseries, FluorescenceTrace):
158+
if isinstance(vector_timeseries, FluorescenceTrace) and (angle_coords is None):
149159
if (
150160
isinstance(vector_timeseries.angle, np.ndarray)
151161
and (vector_timeseries.angle.dtype == np.complex128)
@@ -156,7 +166,12 @@ def pva(
156166
and all(x is not None for x in vector_timeseries.angle)
157167
):
158168
angle_coords = np.exp(-1j*vector_timeseries.angle)
169+
elif angle_coords is None:
170+
angle_coords = np.exp(np.linspace(np.pi, -np.pi, vector_timeseries.shape[0])*1j) # it goes clockwise.
159171

172+
if angle_coords.dtype != np.complex128:
173+
angle_coords = np.exp(1j*angle_coords)
174+
160175
pva_val = np.asarray(
161176
np.matmul(
162177
angle_coords,

siffpy/siffmath/phase/traces.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ def invert(self):
172172
"""
173173
self[...] = 1.0/self
174174

175-
def inverted(self)->np.ndarray:
175+
def inverted(self)->'PhaseTrace':
176176
"""
177177
Returns a _new_ PhaseTrace object with the phase inverted without
178178
modifying the existing trace.

0 commit comments

Comments
 (0)