pytme 0.2.1__cp311-cp311-macosx_14_0_arm64.whl → 0.2.3__cp311-cp311-macosx_14_0_arm64.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.
Files changed (52) hide show
  1. {pytme-0.2.1.data → pytme-0.2.3.data}/scripts/match_template.py +219 -216
  2. {pytme-0.2.1.data → pytme-0.2.3.data}/scripts/postprocess.py +86 -54
  3. pytme-0.2.3.data/scripts/preprocess.py +132 -0
  4. {pytme-0.2.1.data → pytme-0.2.3.data}/scripts/preprocessor_gui.py +181 -94
  5. pytme-0.2.3.dist-info/METADATA +92 -0
  6. pytme-0.2.3.dist-info/RECORD +75 -0
  7. {pytme-0.2.1.dist-info → pytme-0.2.3.dist-info}/WHEEL +1 -1
  8. pytme-0.2.1.data/scripts/preprocess.py → scripts/eval.py +1 -1
  9. scripts/extract_candidates.py +20 -13
  10. scripts/match_template.py +219 -216
  11. scripts/match_template_filters.py +154 -95
  12. scripts/postprocess.py +86 -54
  13. scripts/preprocess.py +95 -56
  14. scripts/preprocessor_gui.py +181 -94
  15. scripts/refine_matches.py +265 -61
  16. tme/__init__.py +0 -1
  17. tme/__version__.py +1 -1
  18. tme/analyzer.py +458 -813
  19. tme/backends/__init__.py +40 -11
  20. tme/backends/_jax_utils.py +187 -0
  21. tme/backends/cupy_backend.py +109 -226
  22. tme/backends/jax_backend.py +230 -152
  23. tme/backends/matching_backend.py +445 -384
  24. tme/backends/mlx_backend.py +32 -59
  25. tme/backends/npfftw_backend.py +240 -507
  26. tme/backends/pytorch_backend.py +30 -151
  27. tme/density.py +248 -371
  28. tme/extensions.cpython-311-darwin.so +0 -0
  29. tme/matching_data.py +328 -284
  30. tme/matching_exhaustive.py +195 -1499
  31. tme/matching_optimization.py +143 -106
  32. tme/matching_scores.py +887 -0
  33. tme/matching_utils.py +287 -388
  34. tme/memory.py +377 -0
  35. tme/orientations.py +78 -21
  36. tme/parser.py +3 -4
  37. tme/preprocessing/_utils.py +61 -32
  38. tme/preprocessing/composable_filter.py +7 -4
  39. tme/preprocessing/compose.py +7 -3
  40. tme/preprocessing/frequency_filters.py +49 -39
  41. tme/preprocessing/tilt_series.py +44 -72
  42. tme/preprocessor.py +560 -526
  43. tme/structure.py +491 -188
  44. tme/types.py +5 -3
  45. pytme-0.2.1.dist-info/METADATA +0 -73
  46. pytme-0.2.1.dist-info/RECORD +0 -73
  47. tme/helpers.py +0 -881
  48. tme/matching_constrained.py +0 -195
  49. {pytme-0.2.1.data → pytme-0.2.3.data}/scripts/estimate_ram_usage.py +0 -0
  50. {pytme-0.2.1.dist-info → pytme-0.2.3.dist-info}/LICENSE +0 -0
  51. {pytme-0.2.1.dist-info → pytme-0.2.3.dist-info}/entry_points.txt +0 -0
  52. {pytme-0.2.1.dist-info → pytme-0.2.3.dist-info}/top_level.txt +0 -0
scripts/refine_matches.py CHANGED
@@ -8,16 +8,21 @@
8
8
  import argparse
9
9
  import subprocess
10
10
  from sys import exit
11
+ from time import time
11
12
  from shutil import copyfile
12
13
  from typing import Tuple, List, Dict
13
14
 
14
15
  import numpy as np
15
16
  from scipy import optimize
17
+ from sklearn.metrics import roc_auc_score
16
18
 
17
19
  from tme import Orientations, Density
18
- from tme.matching_utils import generate_tempfile_name, load_pickle
20
+ from tme.matching_utils import generate_tempfile_name, load_pickle, write_pickle, create_mask
19
21
  from tme.matching_exhaustive import MATCHING_EXHAUSTIVE_REGISTER
20
22
 
23
+ def parse_range(x : str):
24
+ start, stop,step = x.split(":")
25
+ return range(int(start), int(stop), int(step))
21
26
 
22
27
  def parse_args():
23
28
  parser = argparse.ArgumentParser(
@@ -30,13 +35,10 @@ def parse_args():
30
35
  type=str,
31
36
  help="Path to an orientations file in a supported format. See "
32
37
  "https://kosinskilab.github.io/pyTME/reference/api/tme.orientations.Orientations.from_file.html"
33
- " for available options."
38
+ " for available options.",
34
39
  )
35
40
  io_group.add_argument(
36
- "--output_prefix",
37
- required=True,
38
- type=str,
39
- help="Path to write output to."
41
+ "--output_prefix", required=True, type=str, help="Path to write output to."
40
42
  )
41
43
  io_group.add_argument(
42
44
  "--iterations",
@@ -187,6 +189,28 @@ def parse_args():
187
189
  default=False,
188
190
  help="Optimize template matching highpass filter cutoff.",
189
191
  )
192
+ optimization_group.add_argument(
193
+ "--lowpass-range",
194
+ dest="lowpass_range",
195
+ type=str,
196
+ default="0:50:5",
197
+ help="Optimize template matching lowpass filter cutoff.",
198
+ )
199
+ optimization_group.add_argument(
200
+ "--highpass-range",
201
+ dest="highpass_range",
202
+ type=str,
203
+ default="0:50:5",
204
+ help="Optimize template matching highpass filter cutoff.",
205
+ )
206
+ optimization_group.add_argument(
207
+ "--translation-uncertainty",
208
+ dest="translation_uncertainty",
209
+ type=int,
210
+ default=None,
211
+ help="Optimize template matching highpass filter cutoff.",
212
+ )
213
+
190
214
 
191
215
  args = parser.parse_args()
192
216
 
@@ -200,6 +224,14 @@ def parse_args():
200
224
  "Please specific either --input_file or --target and --template."
201
225
  )
202
226
 
227
+ if args.lowpass_range != "None":
228
+ args.lowpass_range = parse_range(args.lowpass_range)
229
+ else:
230
+ args.lowpass_range = (None, )
231
+ if args.highpass_range != "None":
232
+ args.highpass_range = parse_range(args.highpass_range)
233
+ else:
234
+ args.highpass_range = (None, )
203
235
  return args
204
236
 
205
237
 
@@ -218,10 +250,20 @@ def argdict_to_command(input_args: Dict, executable: str) -> List:
218
250
  ret.insert(0, executable)
219
251
  return " ".join(ret)
220
252
 
253
+ def run_command(command):
254
+ ret = subprocess.run(command, capture_output=True, shell=True)
255
+ if ret.returncode != 0:
256
+ print(f"Error when executing: {command}.")
257
+ print(f"Stdout: {ret.stdout.decode('utf-8')}")
258
+ print(f"Stderr: {ret.stderr.decode('utf-8')}")
259
+ exit(-1)
260
+
261
+ return None
221
262
 
222
263
  def create_stacking_argdict(args) -> Dict:
223
264
  arg_dict = {
224
- "--input_file": args.input_file,
265
+ "--target": args.target,
266
+ "--template": args.template,
225
267
  "--orientations": args.orientations,
226
268
  "--output_file": args.candidate_stack_path,
227
269
  "--keep_out_of_box": args.keep_out_of_box,
@@ -234,7 +276,6 @@ def create_matching_argdict(args) -> Dict:
234
276
  "--target": args.target,
235
277
  "--template": args.template,
236
278
  "--template_mask": args.template_mask,
237
- "--target_mask": args.target_mask,
238
279
  "-o": args.match_template_path,
239
280
  "-a": args.angular_sampling,
240
281
  "-s": args.score,
@@ -256,36 +297,35 @@ def create_postprocessing_argdict(args) -> Dict:
256
297
  "--peak_caller": "PeakCallerMaximumFilter",
257
298
  "--number_of_peaks": args.number_of_peaks,
258
299
  "--output_format": "orientations",
259
- "--mask_edges" : True,
300
+ "--mask_edges": True,
260
301
  }
302
+ if args.target_mask is not None:
303
+ arg_dict["--mask_edges"] = False
261
304
  return arg_dict
262
305
 
263
- def update_orientations(old, new, args):
264
- stack_shape = Density.from_file(
265
- args.candidate_stack_path, use_memmap = True
266
- ).shape
267
-
268
- stack_center = np.add(
269
- np.divide(stack_shape, 2).astype(int),
270
- np.mod(stack_shape, 2)
271
- )
272
306
 
273
- print(old.translations[new.translations[:, 0].astype(int)])
274
- print(new.translations[:, 1:], stack_center, stack_shape)
307
+ def update_orientations(old : Orientations, new : Orientations, args, **kwargs) -> Orientations:
308
+ stack_shape = Density.from_file(args.candidate_stack_path, use_memmap=True).shape
309
+ stack_center = np.add(np.divide(stack_shape, 2).astype(int), np.mod(stack_shape, 2))
275
310
 
276
- new.translations[:, 1:] = np.add(
277
- old.translations[new.translations[:, 0].astype(int)],
278
- np.subtract(new.translations, stack_center)[:, 1:]
311
+ peak_number = new.translations[:, 0].astype(int)
312
+ new_translations = np.add(
313
+ old.translations[peak_number],
314
+ np.subtract(new.translations, stack_center)[:, 1:],
279
315
  )
280
- print(new.translations)
281
- # The effect of --align_orientations should be handled herer
282
- return new
283
-
316
+ ret = old.copy()
317
+ ret.scores[:] = 0
318
+ ret.scores[peak_number] = new.scores
319
+ ret.translations[peak_number] = new_translations
284
320
 
321
+ # The effect of --align_orientations should be handled herer
322
+ return ret
285
323
 
286
324
 
287
325
  class DeepMatcher:
288
- def __init__(self, args):
326
+ def __init__(self, args, margin : float = 0.5):
327
+ self.args = args
328
+ self.margin = margin
289
329
  self.orientations = Orientations.from_file(args.orientations)
290
330
 
291
331
  match_template_args = create_matching_argdict(args)
@@ -297,62 +337,172 @@ class DeepMatcher:
297
337
  self.filter_parameters["--lowpass"] = 0
298
338
  if args.highpass:
299
339
  self.filter_parameters["--highpass"] = 200
340
+ # self.filter_parameters["--whiten"] = False
341
+ self.filter_parameters["--no_filter_target"] = False
342
+
300
343
 
301
344
  self.postprocess_args = create_postprocessing_argdict(args)
302
345
  self.postprocess_args["--number_of_peaks"] = 1
303
346
 
304
-
305
347
  def get_initial_values(self) -> Tuple[float]:
306
348
  ret = tuple(float(x) for x in self.filter_parameters.values())
307
349
  return ret
308
350
 
309
351
  def format_parameters(self, parameter_values: Tuple[float]) -> Dict:
310
352
  ret = {}
311
- for value, key in zip(parameter_values, ret.keys()):
353
+ for value, key in zip(parameter_values, self.filter_parameters.keys()):
312
354
  ret[key] = value
313
355
  if isinstance(self.filter_parameters[key], bool):
314
356
  ret[key] = value > 0.5
315
357
  return ret
316
358
 
359
+ def forward(self, x : Tuple[float]):
360
+
361
+
362
+ # Label 1 -> True positive, label 0 -> false positive
363
+ orientations_new = self(x)
364
+ label, score = orientations_new.details, orientations_new.scores
365
+ # loss = np.add(
366
+ # (1 - label) * np.square(score),
367
+ # label * np.square(np.fmax(self.margin - score, 0.0))
368
+ # )
369
+ # loss = loss.mean()
370
+
371
+
372
+
373
+ loss = roc_auc_score(label, score)
374
+ # print(
375
+ # np.mean(score[label == 1]), np.mean(score[label == 0]),
376
+ # *x, loss, time()
377
+ # )
378
+
379
+ return loss
380
+
317
381
  def __call__(self, x: Tuple[float]):
318
382
  filter_parameters = self.format_parameters(x)
319
383
  self.match_template_args.update(filter_parameters)
320
384
  match_template = argdict_to_command(
321
385
  self.match_template_args,
322
- executable="python3 /Users/vmaurer/src/pytme/scripts/match_template_filters.py"
386
+ executable="python3 $HOME/src/pytme/scripts/match_template_filters.py",
323
387
  )
324
- _ = subprocess.run(match_template, capture_output=True, shell = True)
388
+ run_command(match_template)
325
389
 
326
390
  # Assume we get a new peak for each input in the same order
327
391
  postprocess = argdict_to_command(
328
392
  self.postprocess_args,
329
- executable="python3 /Users/vmaurer/src/pytme/scripts/postprocess.py"
393
+ executable="python3 $HOME/src/pytme/scripts/postprocess.py",
394
+ )
395
+ run_command(postprocess)
396
+
397
+ orientations_new = Orientations.from_file(
398
+ f"{self.postprocess_args['--output_prefix']}.tsv"
399
+ )
400
+ orientations_new = update_orientations(
401
+ new=orientations_new,
402
+ old=self.orientations,
403
+ args=self.args
404
+ )
405
+
406
+ label, score = orientations_new.details, orientations_new.scores
407
+ loss = roc_auc_score(label, score)
408
+ print(
409
+ np.mean(score[label == 1]), np.mean(score[label == 0]),
410
+ *x, 0, loss, time()
411
+ )
412
+
413
+
414
+ # Rerun with noise correction
415
+ temp_args = self.match_template_args.copy()
416
+ background_file = generate_tempfile_name(".pickle")
417
+ temp_args["--scramble_phases"] = True
418
+ temp_args["-o"] = background_file
419
+ match_template = argdict_to_command(
420
+ temp_args,
421
+ executable="python3 $HOME/src/pytme/scripts/match_template_filters.py",
422
+ )
423
+ run_command(match_template)
424
+ temp_args = self.match_template_args.copy()
425
+ temp_args["--background_file"] = background_file
426
+ postprocess = argdict_to_command(
427
+ self.postprocess_args,
428
+ executable="python3 $HOME/src/pytme/scripts/postprocess.py",
330
429
  )
331
- _ = subprocess.run(postprocess, capture_output=True, shell = True)
430
+ run_command(postprocess)
332
431
 
333
432
  orientations_new = Orientations.from_file(
334
433
  f"{self.postprocess_args['--output_prefix']}.tsv"
335
434
  )
336
- orientations_new = orientations_new[np.argsort(orientations_new.translations[:, 0])]
435
+ orientations_new = update_orientations(
436
+ new=orientations_new,
437
+ old=self.orientations,
438
+ args=self.args
439
+ )
337
440
 
338
- label, dist = self.orientations.details, orientations_new.scores
339
- loss = np.add(
340
- (1 - label) * np.square(dist),
341
- label * np.square(np.fmax(.5 - dist, 0.0))
441
+ label, score = orientations_new.details, orientations_new.scores
442
+ loss = roc_auc_score(label, score)
443
+ print(
444
+ np.mean(score[label == 1]), np.mean(score[label == 0]),
445
+ *x, 1, loss, time()
342
446
  )
343
- return np.sum(loss)
344
447
 
448
+ return orientations_new
449
+
450
+ # def __call__(self, x: Tuple[float]):
451
+ # filter_parameters = self.format_parameters(x)
452
+ # # print(filter_parameters)
453
+ # self.match_template_args.update(filter_parameters)
454
+ # match_template = argdict_to_command(
455
+ # self.match_template_args,
456
+ # executable="python3 $HOME/src/pytme/scripts/match_template_filters.py",
457
+ # )
458
+ # run_command(match_template)
459
+
460
+ # data = load_pickle(self.args.match_template_path)
461
+ # temp_args = self.match_template_args.copy()
462
+ # temp_args["--scramble_phases"] = True
463
+ # # write_pickle(data, "/home/vmaurer/deep_matching/t.pickle")
464
+
465
+ # match_template = argdict_to_command(
466
+ # temp_args,
467
+ # executable="python3 $HOME/src/pytme/scripts/match_template_filters.py",
468
+ # )
469
+ # run_command(match_template)
470
+ # data_norm = load_pickle(self.args.match_template_path)
471
+ # # write_pickle(data_norm, "/home/vmaurer/deep_matching/noise.pickle")
472
+
473
+ # data[0] = (data[0] - data_norm[0]) / (1 - data_norm[0])
474
+ # data[0] = np.fmax(data[0], 0)
475
+ # write_pickle(data, self.args.match_template_path)
476
+
477
+ # # Assume we get a new peak for each input in the same order
478
+ # postprocess = argdict_to_command(
479
+ # self.postprocess_args,
480
+ # executable="python3 $HOME/src/pytme/scripts/postprocess.py",
481
+ # )
482
+ # run_command(postprocess)
483
+
484
+ # orientations_new = Orientations.from_file(
485
+ # f"{self.postprocess_args['--output_prefix']}.tsv"
486
+ # )
487
+ # orientations_new = update_orientations(
488
+ # new=orientations_new,
489
+ # old=self.orientations,
490
+ # args=self.args
491
+ # )
492
+
493
+ # return orientations_new
345
494
 
346
495
 
347
496
  def main():
497
+ print("Entered")
348
498
  args = parse_args()
349
499
 
350
500
  if args.input_file is not None:
351
501
  data = load_pickle(args.input_file)
352
502
  target_origin, _, sampling_rate, cli_args = data[-1]
353
- args.target, args.template = cli_args.target, cli_args.template
503
+ args.target, args.template = cli_args.target, cli_args.template
354
504
 
355
- args.candidate_stack_path = generate_tempfile_name(suffix = ".h5")
505
+ args.candidate_stack_path = generate_tempfile_name(suffix=".h5")
356
506
  args.new_orientations_path = generate_tempfile_name()
357
507
  args.match_template_path = generate_tempfile_name()
358
508
 
@@ -364,47 +514,100 @@ def main():
364
514
  create_image_stack = create_stacking_argdict(args)
365
515
  create_image_stack = argdict_to_command(
366
516
  create_image_stack,
367
- executable="python3 /Users/vmaurer/src/pytme/scripts/extract_candidates.py"
517
+ executable="python3 $HOME/src/pytme/scripts/extract_candidates.py",
368
518
  )
369
- _ = subprocess.run(create_image_stack, capture_output=True, shell = True)
519
+ run_command(create_image_stack)
520
+
521
+ print("Created image stack")
370
522
  if args.verbose:
371
523
  copyfile(args.candidate_stack_path, f"{args.output_prefix}_stack.h5")
372
- _ = match_deep(x = ())
524
+
525
+ print("Starting matching")
526
+ orientations = match_deep(x=())
527
+
373
528
  if args.verbose:
374
529
  copyfile(args.match_template_path, f"{args.output_prefix}_stack.pickle")
375
- orientations = Orientations.from_file(f"{args.new_orientations_path}.tsv")
376
- orientations = update_orientations(
377
- new = orientations,
378
- old = Orientations.from_file(args.orientations),
379
- args= args
380
- )
530
+ print("Completed matching")
381
531
  orientations.to_file(f"{args.output_prefix}.tsv")
382
532
  exit(0)
383
533
 
534
+ if args.translation_uncertainty is not None:
535
+ args.target_mask = generate_tempfile_name(suffix=".h5")
536
+
384
537
  for current_iteration in range(args.iterations):
385
538
  create_image_stack = create_stacking_argdict(args)
386
539
  create_image_stack = argdict_to_command(
387
540
  create_image_stack,
388
- executable="python3 /Users/vmaurer/src/pytme/scripts/extract_candidates.py"
541
+ executable="python3 $HOME/src/pytme/scripts/extract_candidates.py",
389
542
  )
390
- _ = subprocess.run(create_image_stack, capture_output=True, shell = True)
543
+ run_command(create_image_stack)
544
+
545
+ if args.translation_uncertainty is not None:
546
+ dens = Density.from_file(args.candidate_stack_path)
547
+ stack_center = np.add(
548
+ np.divide(dens.data.shape, 2).astype(int), np.mod(dens.data.shape, 2)
549
+ ).astype(int)[1:]
550
+
551
+ out = dens.empty
552
+ out.data[:,...] = create_mask(
553
+ mask_type = "ellipse",
554
+ center = stack_center,
555
+ radius = args.translation_uncertainty,
556
+ shape = dens.data.shape[1:]
557
+ )
558
+ out.to_file(args.target_mask)
559
+
560
+
391
561
 
392
562
  # Perhaps we need a different optimizer here to use sensible steps for each parameter
563
+ parameters, min_loss = (), None
393
564
  match_deep = DeepMatcher(args)
394
- parameters = optimize.minimize(
395
- x0=match_deep.get_initial_values(),
396
- fun=match_deep,
397
- options = {"maxiter" : 2}
398
- )
565
+ # for lowpass in (0, 10, 20, 50):
566
+ # for highpass in (50, 100, 150, 200):
567
+ # for whiten in (False, True):
568
+ # loss = match_deep.forward((lowpass, highpass, whiten))
569
+ # # print((lowpass, highpass), loss)
570
+ # if min_loss is None:
571
+ # min_loss = loss
572
+ # if loss < min_loss:
573
+ # min_loss = loss
574
+ # parameters = (lowpass, highpass, whiten),
575
+
576
+ # for lowpass in (10, 50, 100, 200):
577
+ # for highpass in (10, 50, 100, 200):
578
+ for lowpass in args.lowpass_range:
579
+ for highpass in args.highpass_range:
580
+ if lowpass is not None and highpass is not None:
581
+ if lowpass >= highpass:
582
+ continue
583
+ for no_filter_target in (True, False):
584
+ loss = match_deep.forward((lowpass, highpass, no_filter_target))
585
+ if min_loss is None:
586
+ min_loss = loss
587
+ if loss < min_loss:
588
+ min_loss = loss
589
+ parameters = (lowpass, highpass, no_filter_target)
590
+
591
+ # print("Final output", min_loss, parameters)
592
+ import sys
593
+ sys.exit(0)
594
+
595
+ # parameters = optimize.minimize(
596
+ # x0=match_deep.get_initial_values(),
597
+ # fun=match_deep.forward,
598
+ # method="L-BFGS-B",
599
+ # options={"maxiter": 100}
600
+ # )
399
601
  parameter_dict = match_deep.format_parameters(parameters)
602
+ print("Converged with parameters", parameters)
400
603
 
401
604
  match_template = create_matching_argdict(args)
402
605
  match_template.update(parameter_dict)
403
606
  match_template = argdict_to_command(
404
607
  match_template,
405
- executable="python3 /Users/vmaurer/src/pytme/scripts/match_template_filters.py"
608
+ executable="python3 $HOME/src/pytme/scripts/match_template_filters.py",
406
609
  )
407
- _ = subprocess.run(match_template, capture_output=True, shell = True)
610
+ _ = subprocess.run(match_template, capture_output=True, shell=True)
408
611
 
409
612
  # Some form of labelling is necessary for these matches
410
613
  # 1. All of them are true positives
@@ -413,10 +616,11 @@ def main():
413
616
  # 4. Perhaps also sensible to include a certain percentage of low scores as true negatives
414
617
  postprocess = create_postprocessing_argdict(args)
415
618
  postprocess = argdict_to_command(postprocess, executable="postprocess.py")
416
- _ = subprocess.run(postprocess, capture_output=True, shell = True)
619
+ _ = subprocess.run(postprocess, capture_output=True, shell=True)
417
620
  args.orientations = f"{args.new_orientations_path}.tsv"
418
621
  orientations = Orientations.from_file(args.orientations)
419
622
  orientations.to_file(f"{args.output_prefix}_{current_iteration}.tsv")
420
623
 
624
+
421
625
  if __name__ == "__main__":
422
626
  main()
tme/__init__.py CHANGED
@@ -4,4 +4,3 @@ from .density import Density
4
4
  from .preprocessor import Preprocessor
5
5
  from .structure import Structure
6
6
  from .orientations import Orientations
7
- from .matching_optimization import FitRefinement
tme/__version__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.2.1"
1
+ __version__ = "0.2.3"