phaserEM 0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- phaser/__init__.py +0 -0
- phaser/__main__.py +5 -0
- phaser/engines/common/__init__.py +0 -0
- phaser/engines/common/noise_models.py +113 -0
- phaser/engines/common/output.py +189 -0
- phaser/engines/common/position_correction.py +62 -0
- phaser/engines/common/regularizers.py +403 -0
- phaser/engines/common/simulation.py +270 -0
- phaser/engines/conventional/__init__.py +0 -0
- phaser/engines/conventional/run.py +142 -0
- phaser/engines/conventional/solvers.py +476 -0
- phaser/engines/gradient/run.py +451 -0
- phaser/engines/gradient/solvers.py +139 -0
- phaser/execute.py +371 -0
- phaser/hooks/__init__.py +158 -0
- phaser/hooks/hook.py +159 -0
- phaser/hooks/io/empad.py +88 -0
- phaser/hooks/object.py +25 -0
- phaser/hooks/preprocessing.py +133 -0
- phaser/hooks/probe.py +24 -0
- phaser/hooks/regularization.py +97 -0
- phaser/hooks/scan.py +27 -0
- phaser/hooks/schedule.py +75 -0
- phaser/hooks/solver.py +169 -0
- phaser/io/__init__.py +0 -0
- phaser/io/empad.py +212 -0
- phaser/main.py +92 -0
- phaser/plan.py +184 -0
- phaser/py.typed +0 -0
- phaser/state.py +249 -0
- phaser/types.py +305 -0
- phaser/utils/__init__.py +0 -0
- phaser/utils/_cuda_kernels.py +213 -0
- phaser/utils/_jax_kernels.py +98 -0
- phaser/utils/analysis.py +263 -0
- phaser/utils/image.py +201 -0
- phaser/utils/io.py +402 -0
- phaser/utils/misc.py +295 -0
- phaser/utils/num.py +800 -0
- phaser/utils/object.py +578 -0
- phaser/utils/optics.py +377 -0
- phaser/utils/physics.py +88 -0
- phaser/utils/plotting.py +699 -0
- phaser/utils/scan.py +60 -0
- phaser/web/__init__.py +0 -0
- phaser/web/dist/03510a839ccb97b0da9f.module.wasm +0 -0
- phaser/web/dist/9573273f862f4f5d9644.module.wasm +0 -0
- phaser/web/dist/bundle-dashboard.js +712 -0
- phaser/web/dist/bundle-manager.js +210 -0
- phaser/web/dist/bundle-vendors-node_modules_wasm-array_wasm_array_js.js +106 -0
- phaser/web/dist/style.css +152 -0
- phaser/web/notebook.py +269 -0
- phaser/web/routes.py +211 -0
- phaser/web/server.py +540 -0
- phaser/web/slurm.py +180 -0
- phaser/web/templates/base.html +13 -0
- phaser/web/templates/dashboard.html +14 -0
- phaser/web/templates/manager.html +19 -0
- phaser/web/types.py +255 -0
- phaser/web/util.py +116 -0
- phaser/web/worker.py +200 -0
- phaserem-0.1.dist-info/METADATA +121 -0
- phaserem-0.1.dist-info/RECORD +67 -0
- phaserem-0.1.dist-info/WHEEL +5 -0
- phaserem-0.1.dist-info/entry_points.txt +2 -0
- phaserem-0.1.dist-info/licenses/LICENSE.txt +373 -0
- phaserem-0.1.dist-info/top_level.txt +1 -0
phaser/io/empad.py
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
import re
|
|
4
|
+
import typing as t
|
|
5
|
+
|
|
6
|
+
import numpy
|
|
7
|
+
import pane
|
|
8
|
+
import pane.io
|
|
9
|
+
from numpy.typing import NDArray
|
|
10
|
+
from pane.annotations import shape
|
|
11
|
+
from pane.convert import IntoConverterHandlers
|
|
12
|
+
from typing_extensions import Self
|
|
13
|
+
|
|
14
|
+
from phaser.types import IsVersion
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _get_dir(f: pane.io.FileOrPath) -> t.Optional[Path]:
|
|
18
|
+
if isinstance(f, (str, Path)):
|
|
19
|
+
return Path(f).parent
|
|
20
|
+
|
|
21
|
+
name = getattr(f, 'name', None)
|
|
22
|
+
if name in (None, '<stdout>', '<stderr>'):
|
|
23
|
+
return None
|
|
24
|
+
path = Path(name)
|
|
25
|
+
return path.parent if path.exists() else None
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class EmpadMetadata(pane.PaneBase, frozen=False, kw_only=True, allow_extra=True):
|
|
29
|
+
file_type: t.Literal['pyMultislicer_metadata', 'empad_metadata'] = 'empad_metadata'
|
|
30
|
+
|
|
31
|
+
@classmethod
|
|
32
|
+
def from_json(cls, f: pane.io.FileOrPath, *,
|
|
33
|
+
custom: t.Optional[IntoConverterHandlers] = None) -> Self:
|
|
34
|
+
path = _get_dir(f)
|
|
35
|
+
self = pane.io.from_json(f, cls, custom=custom)
|
|
36
|
+
object.__setattr__(self, 'path', path)
|
|
37
|
+
return self
|
|
38
|
+
|
|
39
|
+
def __post_init__(self):
|
|
40
|
+
object.__setattr__(self, 'path', None)
|
|
41
|
+
|
|
42
|
+
name: str
|
|
43
|
+
"""Experiment name"""
|
|
44
|
+
|
|
45
|
+
version: t.Annotated[str, IsVersion(exactly="2.0")] = "2.0"
|
|
46
|
+
"""Metadata version"""
|
|
47
|
+
|
|
48
|
+
raw_filename: str
|
|
49
|
+
"""Raw 4DSTEM data filename, relative to metadata location."""
|
|
50
|
+
|
|
51
|
+
orig_path: t.Optional[Path] = None
|
|
52
|
+
"""Original path to experimental folder."""
|
|
53
|
+
|
|
54
|
+
path: t.Optional[Path] = pane.field(init=False, exclude=True)
|
|
55
|
+
"""Current path to experimental folder (based on metadata loading)"""
|
|
56
|
+
|
|
57
|
+
author: t.Optional[str] = None
|
|
58
|
+
"""Author of dataset"""
|
|
59
|
+
time: t.Optional[str] = None
|
|
60
|
+
"""Image acquisition time (RFC 2822 format)"""
|
|
61
|
+
time_unix: t.Optional[float] = None
|
|
62
|
+
"""Image acquisition time (seconds since Unix epoch)"""
|
|
63
|
+
bg_unix: t.Optional[float] = None
|
|
64
|
+
"""Background image acquisition time (seconds since Unix epoch)"""
|
|
65
|
+
has_bg: t.Optional[bool] = None
|
|
66
|
+
"""Whether background image is valid"""
|
|
67
|
+
|
|
68
|
+
voltage: float
|
|
69
|
+
"""Accelerating voltage (V)."""
|
|
70
|
+
conv_angle: t.Optional[float] = None
|
|
71
|
+
"""Convergence angle (mrad)."""
|
|
72
|
+
defocus: t.Optional[float] = None
|
|
73
|
+
"""Defocus (m). Positive is overfocus."""
|
|
74
|
+
camera_length: t.Optional[float] = None
|
|
75
|
+
"""Camera length (m)."""
|
|
76
|
+
diff_step: t.Optional[float] = None
|
|
77
|
+
"""Diffraction pixel size (mrad/px)."""
|
|
78
|
+
|
|
79
|
+
scan_rotation: float
|
|
80
|
+
"""Scan rotation (degrees)."""
|
|
81
|
+
scan_shape: t.Tuple[int, int]
|
|
82
|
+
"""Scan shape (x, y)."""
|
|
83
|
+
scan_fov: t.Tuple[float, float]
|
|
84
|
+
"""Scan field of view (m)."""
|
|
85
|
+
scan_step: t.Tuple[float, float]
|
|
86
|
+
"""Scan step (m/px)."""
|
|
87
|
+
|
|
88
|
+
exposure_time: t.Optional[float] = None
|
|
89
|
+
"""Pixel exposure time (s)."""
|
|
90
|
+
post_exposure_time: t.Optional[float] = None
|
|
91
|
+
"""Pixel post-exposure time (s)."""
|
|
92
|
+
beam_current: t.Optional[float] = None
|
|
93
|
+
"""Approx. beam current (A)."""
|
|
94
|
+
adu: t.Optional[float] = None
|
|
95
|
+
"""Single-electron intensity (data units)."""
|
|
96
|
+
|
|
97
|
+
scan_correction: t.Optional[t.Annotated[NDArray[numpy.floating], shape((2, 2))]] = None
|
|
98
|
+
"""Scan correction matrix, [x', y'] = scan_correction @ [x, y]"""
|
|
99
|
+
|
|
100
|
+
scan_positions: t.Optional[t.List[t.Tuple[float, float]]] = None
|
|
101
|
+
"""
|
|
102
|
+
Scan position override (m).
|
|
103
|
+
Should be specified as a 1d list of (x, y) positions, in scan order. `scan_correction` is applied to these positions (if present).
|
|
104
|
+
"""
|
|
105
|
+
|
|
106
|
+
notes: t.Optional[str] = None
|
|
107
|
+
|
|
108
|
+
crop: t.Optional[t.Tuple[int, int, int, int]] = None
|
|
109
|
+
"""Region scan is valid within, (min_y, max_y, min_x, max_x). Python-style slicing."""
|
|
110
|
+
|
|
111
|
+
def is_simulated(self) -> bool:
|
|
112
|
+
return self.file_type == "pyMultislicer_metadata"
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def load_4d(path: t.Union[str, Path], scan_shape: t.Optional[t.Tuple[int, int]] = None,
|
|
116
|
+
memmap: bool = False) -> NDArray[numpy.float32]:
|
|
117
|
+
"""
|
|
118
|
+
Load a raw EMPAD dataset into memory.
|
|
119
|
+
|
|
120
|
+
The file is loaded so the dimensions are: (scan_y, scan_x, k_y, k_x), with y decreasing downwards.
|
|
121
|
+
|
|
122
|
+
Patterns are not fftshifted or normalized upon loading.
|
|
123
|
+
|
|
124
|
+
# Parameters
|
|
125
|
+
|
|
126
|
+
- `path`: Path to file to load
|
|
127
|
+
- `scan_shape`: Scan shape of dataset. Will be inferred from the filename if not specified.
|
|
128
|
+
- `memmap`: If specified, memmap the file as opposed to loading it eagerly.
|
|
129
|
+
|
|
130
|
+
Returns a numpy array (or `numpy.memmap`)
|
|
131
|
+
"""
|
|
132
|
+
path = Path(path)
|
|
133
|
+
|
|
134
|
+
if scan_shape is None:
|
|
135
|
+
match = re.search(r"x(\d+)_y(\d+)", path.name)
|
|
136
|
+
if match:
|
|
137
|
+
n_x, n_y = map(int, (match[1], match[2]))
|
|
138
|
+
else:
|
|
139
|
+
raise ValueError(f"Unable to infer probe dimensions from name {path.name}")
|
|
140
|
+
else:
|
|
141
|
+
n_y, n_x = scan_shape
|
|
142
|
+
|
|
143
|
+
if memmap:
|
|
144
|
+
a = numpy.memmap(path, dtype=numpy.float32, mode='r')
|
|
145
|
+
else:
|
|
146
|
+
a = numpy.fromfile(path, dtype=numpy.float32)
|
|
147
|
+
|
|
148
|
+
if not a.size % (130*128) == 0:
|
|
149
|
+
raise ValueError(f"File not divisible by 130x128 (size={a.size}).")
|
|
150
|
+
a.shape = (-1, 130, 128)
|
|
151
|
+
#a = a[:, :128, :]
|
|
152
|
+
|
|
153
|
+
if a.shape[0] != n_x * n_y:
|
|
154
|
+
raise ValueError(f"Got {a.shape[0]} probes, expected {n_x}x{n_y} = {n_x * n_y}.")
|
|
155
|
+
a.shape = (n_y, n_x, *a.shape[1:])
|
|
156
|
+
a = a[..., 127::-1, :] # flip reciprocal y space, crop junk rows
|
|
157
|
+
|
|
158
|
+
return a
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
@t.overload
|
|
162
|
+
def save_4d(arr: NDArray[numpy.float32], *, path: t.Union[str, Path], folder: None = None, name: None = None):
|
|
163
|
+
...
|
|
164
|
+
|
|
165
|
+
@t.overload
|
|
166
|
+
def save_4d(arr: NDArray[numpy.float32], *, path: None = None, folder: t.Union[str, Path], name: t.Optional[str] = None):
|
|
167
|
+
...
|
|
168
|
+
|
|
169
|
+
def save_4d(arr: NDArray[numpy.float32], *, path: t.Union[str, Path, None] = None,
|
|
170
|
+
folder: t.Union[str, Path, None] = None, name: t.Optional[str] = None): #):
|
|
171
|
+
"""
|
|
172
|
+
Save a raw EMPAD dataset.
|
|
173
|
+
|
|
174
|
+
Either `path` or `folder` can be specified. If `folder` is specified,
|
|
175
|
+
`name` will be used as a format string to determine the filename.
|
|
176
|
+
`path` and `folder` cannot be specified simultaneously.
|
|
177
|
+
|
|
178
|
+
Patterns are not fftshifted or normalized upon saving.
|
|
179
|
+
|
|
180
|
+
Parameters:
|
|
181
|
+
- `arr`: Array to save
|
|
182
|
+
- `path`: Path to save dataset to.
|
|
183
|
+
- `folder`: Folder to save dataset inside.
|
|
184
|
+
- `name`: When `folder` is specified, format to use to determine filename. Defaults to `"scan_x{x}_y{y}.raw"`.
|
|
185
|
+
Will be formatted using the scan shape `{'x': n_x, 'y': n_y}`.
|
|
186
|
+
"""
|
|
187
|
+
|
|
188
|
+
try:
|
|
189
|
+
assert len(arr.shape) == 4
|
|
190
|
+
assert arr.shape[2:] == (128, 128)
|
|
191
|
+
except AssertionError as e:
|
|
192
|
+
raise ValueError("Invalid data format") from e
|
|
193
|
+
|
|
194
|
+
if folder is not None:
|
|
195
|
+
if path is not None:
|
|
196
|
+
raise ValueError("Cannot specify both 'path' and 'folder'")
|
|
197
|
+
|
|
198
|
+
n_y, n_x = arr.shape[:2]
|
|
199
|
+
path = Path(folder) / (name or "scan_x{x}_y{y}.raw").format(x=n_x, y=n_y)
|
|
200
|
+
elif path is not None:
|
|
201
|
+
path = Path(path)
|
|
202
|
+
else:
|
|
203
|
+
raise ValueError("Must specify either 'path' or 'folder'")
|
|
204
|
+
|
|
205
|
+
out_shape = list(arr.shape)
|
|
206
|
+
out_shape[2] = 130 # dead rows
|
|
207
|
+
|
|
208
|
+
out = numpy.zeros(out_shape, dtype=numpy.float32)
|
|
209
|
+
out[..., 127::-1, :] = arr.astype(numpy.float32)
|
|
210
|
+
|
|
211
|
+
with open(path, 'wb') as f:
|
|
212
|
+
out.tofile(f)
|
phaser/main.py
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
import sys
|
|
3
|
+
import typing as t
|
|
4
|
+
|
|
5
|
+
import click
|
|
6
|
+
|
|
7
|
+
@click.group()
|
|
8
|
+
def cli():
|
|
9
|
+
pass
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@cli.command('run')
|
|
13
|
+
@click.argument('path', type=click.Path(exists=True, dir_okay=False))
|
|
14
|
+
def run(path: t.Union[str, Path]):
|
|
15
|
+
from .plan import ReconsPlan
|
|
16
|
+
from .execute import execute_plan
|
|
17
|
+
plans = ReconsPlan.from_yaml_all(path)
|
|
18
|
+
|
|
19
|
+
for plan in plans:
|
|
20
|
+
execute_plan(plan)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@cli.command('serve')
|
|
24
|
+
@click.option('--host', type=str, default='localhost')
|
|
25
|
+
@click.option('--port', type=int)
|
|
26
|
+
@click.option('-v', '--verbose', count=True)
|
|
27
|
+
def serve(host: str = 'localhost', port: t.Optional[int] = None, verbose: int = 0):
|
|
28
|
+
from phaser.web.server import server
|
|
29
|
+
|
|
30
|
+
if ':' in host:
|
|
31
|
+
(host, port_from_host) = host.rsplit(':', maxsplit=1)
|
|
32
|
+
try:
|
|
33
|
+
port_from_host = int(port_from_host)
|
|
34
|
+
except ValueError:
|
|
35
|
+
print(f"Invalid host '{host}:{port_from_host}'", file=sys.stderr)
|
|
36
|
+
sys.exit(1)
|
|
37
|
+
|
|
38
|
+
port = port or port_from_host
|
|
39
|
+
|
|
40
|
+
server.run(hostname=host, port=port, verbosity=verbose)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@cli.command('validate')
|
|
44
|
+
@click.argument('path', type=click.Path(allow_dash=True), default='-')
|
|
45
|
+
@click.option('--json/--no-json', default=False)
|
|
46
|
+
def validate(path: t.Union[str, Path], json: bool = False):
|
|
47
|
+
from contextlib import nullcontext
|
|
48
|
+
from .plan import ReconsPlan
|
|
49
|
+
|
|
50
|
+
try:
|
|
51
|
+
if path == '-':
|
|
52
|
+
file = nullcontext(sys.stdin)
|
|
53
|
+
else:
|
|
54
|
+
file = open(Path(path).expanduser(), 'r')
|
|
55
|
+
|
|
56
|
+
with file as file:
|
|
57
|
+
plans = ReconsPlan.from_yaml_all(file)
|
|
58
|
+
except Exception as e:
|
|
59
|
+
print(f"Validation failed:\n{e}", file=sys.stderr)
|
|
60
|
+
|
|
61
|
+
if json:
|
|
62
|
+
from json import dump
|
|
63
|
+
dump({'result': 'error', 'error': str(e)}, sys.stdout)
|
|
64
|
+
print()
|
|
65
|
+
|
|
66
|
+
sys.exit(1)
|
|
67
|
+
|
|
68
|
+
if len(plans) == 1:
|
|
69
|
+
print("Validation of plan successful!", file=sys.stderr)
|
|
70
|
+
else:
|
|
71
|
+
print(f"Validation of {len(plans)} plans successful!", file=sys.stderr)
|
|
72
|
+
|
|
73
|
+
if json:
|
|
74
|
+
from json import dump
|
|
75
|
+
dump({
|
|
76
|
+
'result': 'success',
|
|
77
|
+
'plans': [plan.into_data() for plan in plans],
|
|
78
|
+
}, sys.stdout)
|
|
79
|
+
print()
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@cli.command('worker')
|
|
83
|
+
@click.argument('url', type=str, required=True)
|
|
84
|
+
@click.option('--quiet/--loud', default=False)
|
|
85
|
+
def worker(url: str, quiet: bool = False):
|
|
86
|
+
from phaser.web.worker import run_worker
|
|
87
|
+
|
|
88
|
+
run_worker(url, quiet=quiet)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
if __name__ == '__main__':
|
|
92
|
+
cli()
|
phaser/plan.py
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
import typing as t
|
|
3
|
+
|
|
4
|
+
from .types import Dataclass, Slices, BackendName, Flag, ReconsVars, IsVersion, EmptyDict
|
|
5
|
+
from .hooks import RawDataHook, ProbeHook, ObjectHook, ScanHook, EngineHook, PostInitHook, PostLoadHook
|
|
6
|
+
from .hooks.solver import NoiseModelHook, ConventionalSolverHook, PositionSolverHook, GradientSolverHook
|
|
7
|
+
from .hooks.schedule import FlagLike, ScheduleLike
|
|
8
|
+
from .hooks.regularization import IterConstraintHook, GroupConstraintHook, CostRegularizerHook
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
SaveType: t.TypeAlias = t.Literal[
|
|
12
|
+
'probe', 'probe_mag', 'probe_recip', 'probe_recip_mag',
|
|
13
|
+
'object_phase_stack', 'object_phase_sum',
|
|
14
|
+
'object_mag_stack', 'object_mag_sum',
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class InitPlan(Dataclass, kw_only=True):
|
|
19
|
+
state: t.Optional[Path] = None
|
|
20
|
+
|
|
21
|
+
scan: t.Union[EmptyDict, ScanHook, None] = None
|
|
22
|
+
probe: t.Union[EmptyDict, ProbeHook, None] = None
|
|
23
|
+
object: t.Optional[ObjectHook] = None # ObjectHook('random')
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class SaveOptions(Dataclass, kw_only=True):
|
|
27
|
+
images: t.Tuple[SaveType, ...] = ('probe', 'object_phase_stack')
|
|
28
|
+
crop_roi: bool = True
|
|
29
|
+
unwrap_phase: bool = True
|
|
30
|
+
img_dtype: t.Literal['float', '8bit', '16bit', '32bit'] = '16bit'
|
|
31
|
+
|
|
32
|
+
out_dir: str = "{name}"
|
|
33
|
+
img_fmt: str = "{type}_iter{iter.total_iter}.tiff"
|
|
34
|
+
hdf5_fmt: str = "iter{iter.total_iter}.h5"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class EnginePlan(Dataclass, kw_only=True):
|
|
38
|
+
sim_shape: t.Optional[t.Tuple[int, int]] = None
|
|
39
|
+
resize_method: t.Literal['pad_crop', 'resample'] = 'pad_crop'
|
|
40
|
+
|
|
41
|
+
probe_modes: int = 1
|
|
42
|
+
base_mode_power: float = 0.7
|
|
43
|
+
"""Intensity to assign to the base mode when creating incoherent probe modes."""
|
|
44
|
+
|
|
45
|
+
bwlim_frac: t.Optional[float] = 2/3
|
|
46
|
+
obj_pad_px: float = 5.0
|
|
47
|
+
|
|
48
|
+
slices: t.Optional[Slices] = None
|
|
49
|
+
|
|
50
|
+
niter: int = 10
|
|
51
|
+
grouping: t.Optional[int] = None
|
|
52
|
+
compact: bool = False
|
|
53
|
+
shuffle_groups: t.Optional[FlagLike] = None
|
|
54
|
+
buffer_n_groups: int = 2
|
|
55
|
+
|
|
56
|
+
update_probe: FlagLike = True
|
|
57
|
+
update_object: FlagLike = True
|
|
58
|
+
update_positions: FlagLike = False
|
|
59
|
+
|
|
60
|
+
calc_error: FlagLike = Flag(every=1)
|
|
61
|
+
calc_error_fraction: float = 0.1
|
|
62
|
+
|
|
63
|
+
save: FlagLike = False
|
|
64
|
+
save_images: FlagLike = False
|
|
65
|
+
save_options: SaveOptions = SaveOptions()
|
|
66
|
+
|
|
67
|
+
send_every_group: bool = False
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class AmplitudeNoisePlan(Dataclass, kw_only=True):
|
|
71
|
+
gaussian_variance: float = 0.1
|
|
72
|
+
eps: float = 1.0e-3
|
|
73
|
+
offset: float = 0.0
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class AnscombeNoisePlan(AmplitudeNoisePlan, kw_only=True):
|
|
77
|
+
offset: float = 0.375
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class PoissonNoisePlan(AmplitudeNoisePlan, kw_only=True):
|
|
81
|
+
eps: float = 1.0e-3
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
NoiseModelHook.known['amplitude'] = ('phaser.engines.common.noise_models:AmplitudeNoiseModel', AmplitudeNoisePlan)
|
|
85
|
+
NoiseModelHook.known['anscombe'] = ('phaser.engines.common.noise_models:AnscombeNoiseModel', AnscombeNoisePlan)
|
|
86
|
+
NoiseModelHook.known['poisson'] = ('phaser.engines.common.noise_models:PoissonNoiseModel', PoissonNoisePlan)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class LSQMLSolverPlan(Dataclass, kw_only=True):
|
|
90
|
+
stochastic: bool = True
|
|
91
|
+
|
|
92
|
+
beta_object: ScheduleLike = 1.0
|
|
93
|
+
beta_probe: ScheduleLike = 1.0
|
|
94
|
+
|
|
95
|
+
illum_reg_object: ScheduleLike = 1e-2
|
|
96
|
+
illum_reg_probe: ScheduleLike = 1e-2
|
|
97
|
+
|
|
98
|
+
gamma: ScheduleLike = 1e-4
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class EPIESolverPlan(Dataclass, kw_only=True):
|
|
102
|
+
beta_object: ScheduleLike = 1.0
|
|
103
|
+
beta_probe: ScheduleLike = 1.0
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
ConventionalSolverHook.known['lsqml'] = ('phaser.engines.conventional.solvers:LSQMLSolver', LSQMLSolverPlan)
|
|
107
|
+
ConventionalSolverHook.known['epie'] = ('phaser.engines.conventional.solvers:EPIESolver', EPIESolverPlan)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class ConventionalEnginePlan(EnginePlan, kw_only=True):
|
|
111
|
+
noise_model: NoiseModelHook
|
|
112
|
+
solver: ConventionalSolverHook
|
|
113
|
+
position_solver: t.Optional[PositionSolverHook] = None
|
|
114
|
+
|
|
115
|
+
group_constraints: t.List[GroupConstraintHook]
|
|
116
|
+
iter_constraints: t.List[IterConstraintHook]
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class GradientEnginePlan(EnginePlan):
|
|
120
|
+
noise_model: NoiseModelHook
|
|
121
|
+
solvers: t.Dict[ReconsVars, GradientSolverHook]
|
|
122
|
+
|
|
123
|
+
regularizers: t.List[CostRegularizerHook]
|
|
124
|
+
group_constraints: t.List[GroupConstraintHook]
|
|
125
|
+
iter_constraints: t.List[IterConstraintHook]
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
class SGDSolverPlan(Dataclass, kw_only=True):
|
|
129
|
+
learning_rate: ScheduleLike
|
|
130
|
+
momentum: t.Optional[ScheduleLike] = None
|
|
131
|
+
nesterov: bool = True
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
class AdamSolverPlan(Dataclass, kw_only=True):
|
|
135
|
+
learning_rate: ScheduleLike
|
|
136
|
+
|
|
137
|
+
b1: float = 0.9
|
|
138
|
+
b2: float = 0.999
|
|
139
|
+
eps: float = 1.0e-8
|
|
140
|
+
eps_root: float = 0.0
|
|
141
|
+
|
|
142
|
+
nesterov: bool = False
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
class PolyakSGDSolverPlan(Dataclass, kw_only=True):
|
|
146
|
+
max_learning_rate: ScheduleLike
|
|
147
|
+
f_min: float
|
|
148
|
+
scaling: ScheduleLike = 1.0
|
|
149
|
+
eps: float = 0.0
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
GradientSolverHook.known['sgd'] = ('phaser.engines.gradient.solvers:SGDSolver', SGDSolverPlan)
|
|
153
|
+
GradientSolverHook.known['adam'] = ('phaser.engines.gradient.solvers:AdamSolver', AdamSolverPlan)
|
|
154
|
+
GradientSolverHook.known['polyak_sgd'] = ('phaser.engines.gradient.solvers:PolyakSGDSolver', PolyakSGDSolverPlan)
|
|
155
|
+
|
|
156
|
+
EngineHook.known['conventional'] = ('phaser.engines.conventional.run:run_engine', ConventionalEnginePlan)
|
|
157
|
+
EngineHook.known['gradient'] = ('phaser.engines.gradient.run:run_engine', GradientEnginePlan)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
class ReconsPlan(Dataclass, kw_only=True):
|
|
161
|
+
file_type: t.Literal['phaser_plan'] = 'phaser_plan'
|
|
162
|
+
version: t.Annotated[str, IsVersion(exactly="1.0")] = "1.0"
|
|
163
|
+
|
|
164
|
+
name: str
|
|
165
|
+
|
|
166
|
+
backend: t.Optional[BackendName] = None
|
|
167
|
+
dtype: t.Literal['float32', 'float64'] = 'float32'
|
|
168
|
+
|
|
169
|
+
wavelength: t.Optional[float] = None
|
|
170
|
+
|
|
171
|
+
raw_data: RawDataHook
|
|
172
|
+
|
|
173
|
+
post_load: t.Sequence[PostLoadHook] = ()
|
|
174
|
+
|
|
175
|
+
init: InitPlan = InitPlan()
|
|
176
|
+
|
|
177
|
+
post_init: t.Sequence[PostInitHook] = ()
|
|
178
|
+
|
|
179
|
+
slices: t.Optional[Slices] = None
|
|
180
|
+
|
|
181
|
+
engines: t.List[EngineHook]
|
|
182
|
+
#engines: t.List[t.Annotated[t.Union[ConventionalEngine, GradientEngine], Tagged('type')]]
|
|
183
|
+
|
|
184
|
+
|
phaser/py.typed
ADDED
|
File without changes
|