rsspolymlp 0.4.2__tar.gz → 0.4.3__tar.gz

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 (82) hide show
  1. {rsspolymlp-0.4.2/src/rsspolymlp.egg-info → rsspolymlp-0.4.3}/PKG-INFO +24 -20
  2. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/README.md +23 -19
  3. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/pyproject.toml +1 -1
  4. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/analysis/rss_summarize.py +9 -12
  5. rsspolymlp-0.4.3/src/rsspolymlp/analysis/struct_matcher/assignment_algo.py +181 -0
  6. rsspolymlp-0.4.3/src/rsspolymlp/analysis/struct_matcher/gen_struct_rep.py +371 -0
  7. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/analysis/struct_matcher/identify_uniqstr.py +77 -38
  8. rsspolymlp-0.4.3/src/rsspolymlp/analysis/struct_matcher/redrep_lattice.py +144 -0
  9. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/analysis/struct_matcher/redrep_position.py +174 -115
  10. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/analysis/struct_matcher/struct_match.py +25 -10
  11. rsspolymlp-0.4.3/src/rsspolymlp/analysis/struct_matcher/test/gen_lattice.py +53 -0
  12. rsspolymlp-0.4.3/src/rsspolymlp/analysis/struct_matcher/test/time_measure.py +0 -0
  13. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/api/cli_rsspolymlp_utils.py +34 -28
  14. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/api/rsspolymlp_utils.py +69 -38
  15. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/rss/eliminate_duplicates.py +5 -4
  16. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3/src/rsspolymlp.egg-info}/PKG-INFO +24 -20
  17. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp.egg-info/SOURCES.txt +2 -0
  18. rsspolymlp-0.4.2/src/rsspolymlp/analysis/struct_matcher/assignment_algo.py +0 -112
  19. rsspolymlp-0.4.2/src/rsspolymlp/analysis/struct_matcher/gen_struct_rep.py +0 -325
  20. rsspolymlp-0.4.2/src/rsspolymlp/analysis/struct_matcher/redrep_lattice.py +0 -541
  21. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/LICENSE +0 -0
  22. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/setup.cfg +0 -0
  23. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/__init__.py +0 -0
  24. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/analysis/__init__.py +0 -0
  25. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/analysis/eos.py +0 -0
  26. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/analysis/ghost_minima.py +0 -0
  27. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/analysis/phase_analysis.py +0 -0
  28. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/analysis/struct_matcher/__init__.py +0 -0
  29. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/analysis/struct_matcher/chiral_spg.py +0 -0
  30. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/api/__init__.py +0 -0
  31. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/api/cli_rsspolymlp.py +0 -0
  32. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/api/cli_rsspolymlp_devkit.py +0 -0
  33. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/api/cli_rsspolymlp_plot.py +0 -0
  34. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/api/rsspolymlp.py +0 -0
  35. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/api/rsspolymlp_devkit.py +0 -0
  36. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/api/rsspolymlp_plot.py +0 -0
  37. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/common/__init__.py +0 -0
  38. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/common/atomic_energy.py +0 -0
  39. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/common/composition.py +0 -0
  40. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/common/convert_dict.py +0 -0
  41. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/common/interface_vasp.py +0 -0
  42. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/common/property.py +0 -0
  43. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/mlp_dev/dataset/compress_vasprun.py +0 -0
  44. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/mlp_dev/dataset/gen_mlp_dataset.py +0 -0
  45. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/mlp_dev/dataset/split_dataset.py +0 -0
  46. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/mlp_dev/estimate_cost.py +0 -0
  47. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/mlp_dev/model_selection/binary/gen_single_model.py +0 -0
  48. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/mlp_dev/model_selection/elemental/gen_hybrid_model.py +0 -0
  49. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/mlp_dev/model_selection/elemental/gen_single_model.py +0 -0
  50. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/mlp_dev/model_selection/n_feature.py +0 -0
  51. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/mlp_dev/model_selection/pypolymlp_gridsearch.py +0 -0
  52. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/mlp_dev/model_selection/reduce_grid.py +0 -0
  53. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/mlp_dev/pareto_opt_mlp.py +0 -0
  54. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/mlp_dev/polymlp_dev.py +0 -0
  55. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/rss/__init__.py +0 -0
  56. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/rss/load_logfile.py +0 -0
  57. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/rss/optimization_mlp.py +0 -0
  58. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/rss/random_struct.py +0 -0
  59. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/utils/__init__.py +0 -0
  60. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/utils/lammps_utils.py +0 -0
  61. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/utils/matplot_util/__init__.py +0 -0
  62. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/utils/matplot_util/custom_plt.py +0 -0
  63. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/utils/matplot_util/examples/__init__.py +0 -0
  64. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/utils/matplot_util/examples/example.py +0 -0
  65. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/utils/matplot_util/examples/template.py +0 -0
  66. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/utils/matplot_util/make_plot.py +0 -0
  67. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/utils/opt_sscha/opt_geometry_sscha.py +0 -0
  68. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/utils/opt_sscha/opt_position_sscha.py +0 -0
  69. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/utils/opt_sscha/run_opt_sscha.py +0 -0
  70. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/utils/opt_sscha/sscha_property.py +0 -0
  71. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/utils/pymatgen_utils.py +0 -0
  72. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/utils/spglib_utils.py +0 -0
  73. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/utils/vasp_util/__init__.py +0 -0
  74. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/utils/vasp_util/api.py +0 -0
  75. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/utils/vasp_util/gen_incar.py +0 -0
  76. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/utils/vasp_util/gen_script.py +0 -0
  77. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp/utils/vasp_util/write_poscar.py +0 -0
  78. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp.egg-info/dependency_links.txt +0 -0
  79. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp.egg-info/entry_points.txt +0 -0
  80. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp.egg-info/requires.txt +0 -0
  81. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/src/rsspolymlp.egg-info/top_level.txt +0 -0
  82. {rsspolymlp-0.4.2 → rsspolymlp-0.4.3}/tests/test_cli_help.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rsspolymlp
3
- Version: 0.4.2
3
+ Version: 0.4.3
4
4
  Summary: A framework for random structure search using polynomial MLPs
5
5
  Author-email: Hayato Wakai <wakai@cms.mtl.kyoto-u.ac.jp>
6
6
  Maintainer-email: Hayato Wakai <wakai@cms.mtl.kyoto-u.ac.jp>
@@ -31,25 +31,9 @@ Dynamic: license-file
31
31
 
32
32
  # Framework for random structure search using polynomial machine learning potentials
33
33
 
34
- ## How to use rsspolymlp
35
-
36
- - [Workflow of RSS with polynomial MLPs](docs/rsspolymlp.md)
37
- - Initial structure generation
38
- - Global RSS with polynomial MLPs
39
- - Unique structure identification and RSS result summarization
40
- - Phase stability analysis
41
- - [Development kit for polynomial MLPs](docs/rsspolymlp_dev.md)
42
- - MLP dataset generation
43
- - DFT dataset division
44
- - Polynomial MLP development
45
- - Pareto-optimal MLP selection
46
- - Python API
47
- - [RSS workflow](docs/api_rsspolymlp.md)
48
- - [VASP calculation utilities](src/rsspolymlp/utils/vasp_util/readme.md)
49
- - Single-point calculation
50
- - Local geometry optimization
51
- - [Matplotlib utilities](src/rsspolymlp/utils/matplot_util/readme.md)
52
-
34
+ `rsspolymlp` is a Python code designed to conduct random structure searches (RSS) using polynomial machine learning potentials (MLPs).
35
+ The code provides functionalities for generating initial random structures, performing geometry optimization using polynomial MLPs, and eliminating duplicate structures.
36
+ The formulation and development of polynomial MLPs are detailed in [`pypolymlp`](https://github.com/sekocha/pypolymlp).
53
37
 
54
38
  ## Installation
55
39
 
@@ -88,6 +72,26 @@ pip install rsspolymlp
88
72
  ```
89
73
 
90
74
 
75
+ ## How to use rsspolymlp
76
+
77
+ - [Workflow of RSS with polynomial MLPs](docs/rsspolymlp.md)
78
+ - Initial structure generation
79
+ - Global RSS with polynomial MLPs
80
+ - Unique structure identification and RSS result summarization
81
+ - Phase stability analysis
82
+ - [Development kit for polynomial MLPs](docs/rsspolymlp_dev.md)
83
+ - MLP dataset generation
84
+ - DFT dataset division
85
+ - Polynomial MLP development
86
+ - Pareto-optimal MLP selection
87
+ - Python API
88
+ - [RSS workflow](docs/api_rsspolymlp.md)
89
+ - [VASP calculation utilities](src/rsspolymlp/utils/vasp_util/readme.md)
90
+ - Single-point calculation
91
+ - Local geometry optimization
92
+ - [Matplotlib utilities](src/rsspolymlp/utils/matplot_util/readme.md)
93
+
94
+
91
95
  ## Citation of rsspolymlp
92
96
 
93
97
  If you use `rsspolymlp` in your study, please cite the following articles.
@@ -1,24 +1,8 @@
1
1
  # Framework for random structure search using polynomial machine learning potentials
2
2
 
3
- ## How to use rsspolymlp
4
-
5
- - [Workflow of RSS with polynomial MLPs](docs/rsspolymlp.md)
6
- - Initial structure generation
7
- - Global RSS with polynomial MLPs
8
- - Unique structure identification and RSS result summarization
9
- - Phase stability analysis
10
- - [Development kit for polynomial MLPs](docs/rsspolymlp_dev.md)
11
- - MLP dataset generation
12
- - DFT dataset division
13
- - Polynomial MLP development
14
- - Pareto-optimal MLP selection
15
- - Python API
16
- - [RSS workflow](docs/api_rsspolymlp.md)
17
- - [VASP calculation utilities](src/rsspolymlp/utils/vasp_util/readme.md)
18
- - Single-point calculation
19
- - Local geometry optimization
20
- - [Matplotlib utilities](src/rsspolymlp/utils/matplot_util/readme.md)
21
-
3
+ `rsspolymlp` is a Python code designed to conduct random structure searches (RSS) using polynomial machine learning potentials (MLPs).
4
+ The code provides functionalities for generating initial random structures, performing geometry optimization using polynomial MLPs, and eliminating duplicate structures.
5
+ The formulation and development of polynomial MLPs are detailed in [`pypolymlp`](https://github.com/sekocha/pypolymlp).
22
6
 
23
7
  ## Installation
24
8
 
@@ -57,6 +41,26 @@ pip install rsspolymlp
57
41
  ```
58
42
 
59
43
 
44
+ ## How to use rsspolymlp
45
+
46
+ - [Workflow of RSS with polynomial MLPs](docs/rsspolymlp.md)
47
+ - Initial structure generation
48
+ - Global RSS with polynomial MLPs
49
+ - Unique structure identification and RSS result summarization
50
+ - Phase stability analysis
51
+ - [Development kit for polynomial MLPs](docs/rsspolymlp_dev.md)
52
+ - MLP dataset generation
53
+ - DFT dataset division
54
+ - Polynomial MLP development
55
+ - Pareto-optimal MLP selection
56
+ - Python API
57
+ - [RSS workflow](docs/api_rsspolymlp.md)
58
+ - [VASP calculation utilities](src/rsspolymlp/utils/vasp_util/readme.md)
59
+ - Single-point calculation
60
+ - Local geometry optimization
61
+ - [Matplotlib utilities](src/rsspolymlp/utils/matplot_util/readme.md)
62
+
63
+
60
64
  ## Citation of rsspolymlp
61
65
 
62
66
  If you use `rsspolymlp` in your study, please cite the following articles.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "rsspolymlp"
7
- version = "0.4.2"
7
+ version = "0.4.3"
8
8
  description = "A framework for random structure search using polynomial MLPs"
9
9
  license = "MIT"
10
10
  authors = [
@@ -9,15 +9,14 @@ from time import time
9
9
  from typing import Optional
10
10
 
11
11
  import numpy as np
12
-
13
12
  from pypolymlp.utils.vasp_utils import write_poscar_file
13
+
14
14
  from rsspolymlp.analysis.ghost_minima import detect_ghost_minima
15
15
  from rsspolymlp.analysis.struct_matcher.gen_struct_rep import (
16
+ StructRepGenerator,
16
17
  StructReps,
17
- generate_struct_reps_parallel,
18
18
  )
19
19
  from rsspolymlp.analysis.struct_matcher.identify_uniqstr import (
20
- UniqueStructIdentifier,
21
20
  identify_unique_structure,
22
21
  )
23
22
  from rsspolymlp.common.composition import compute_composition
@@ -58,7 +57,7 @@ class RSSResultSummarizer:
58
57
  self.update_parent = update_parent
59
58
 
60
59
  self.pressure = None
61
- self.analyzer = UniqueStructIdentifier()
60
+ self.analyzer = None
62
61
 
63
62
  def run_summarize(self):
64
63
  os.makedirs("json", exist_ok=True)
@@ -92,8 +91,6 @@ class RSSResultSummarizer:
92
91
 
93
92
  for comp_tag, res_paths in json_paths_c.items():
94
93
  print(f"Composition {comp_tag}: summarizing...")
95
- self.analyzer = UniqueStructIdentifier()
96
-
97
94
  time_start = time()
98
95
 
99
96
  processed_paths = set()
@@ -196,8 +193,6 @@ class RSSResultSummarizer:
196
193
 
197
194
  for comp_tag, res_paths in json_paths_c.items():
198
195
  print(f"Composition {comp_tag}: summarizing...")
199
- self.analyzer = UniqueStructIdentifier()
200
-
201
196
  if not self.parse_vasp:
202
197
  axis_tol = 0.03
203
198
  pos_tol = 0.03
@@ -210,7 +205,7 @@ class RSSResultSummarizer:
210
205
  print(f" - Processing result file (parent): {res_path}")
211
206
  self.analyzer = identify_unique_structure(
212
207
  rss_results_c[comp_tag][res_path],
213
- standardize_axis=True,
208
+ normalize_axis=True,
214
209
  keep_unique=True,
215
210
  axis_tol=axis_tol,
216
211
  pos_tol=pos_tol,
@@ -264,11 +259,13 @@ class RSSResultSummarizer:
264
259
  r["dupstr_paths"] = set(r["dupstr_paths"])
265
260
 
266
261
  print(" - Converting reduced crystal structure representation...")
267
- struct_reps_list = generate_struct_reps_parallel(
268
- rss_results,
262
+ generator = StructRepGenerator(
269
263
  num_process=self.num_process,
270
264
  backend=self.backend,
271
- symprec_set1=self.symprec_set,
265
+ primitive_symprecs=self.primitive_symprecs,
266
+ )
267
+ struct_reps_list = generator.generate_struct_reps_parallel(
268
+ rss_results,
272
269
  )
273
270
  if self.update_parent:
274
271
  print(" - Eliminating duplicate structures...")
@@ -0,0 +1,181 @@
1
+ import time
2
+
3
+ import numpy as np
4
+ from scipy.sparse import csr_array
5
+ from scipy.sparse.csgraph import maximum_bipartite_matching
6
+
7
+
8
+ def sort_match_screen(
9
+ pos_reps1: np.ndarray,
10
+ pos_reps2: np.ndarray,
11
+ elements1: np.ndarray,
12
+ elements2: np.ndarray,
13
+ axis_abc: np.ndarray,
14
+ pos_tol: float,
15
+ verbose: bool = False,
16
+ ):
17
+ # pos1: (N1, n_atoms, 3)
18
+ # pos2: (N2, n_atoms, 3)
19
+ if len(elements1) != len(elements2):
20
+ return np.zeros((len(pos_reps1), len(pos_reps2)), dtype=bool)
21
+ unique_elements = np.unique(elements1)
22
+
23
+ def sort_positions_axis(pos_reps, elements, axis):
24
+ pos_reps = np.asarray(pos_reps)
25
+ sorted_reps = np.empty_like(pos_reps[:, :, axis])
26
+ start = 0
27
+ for el in unique_elements:
28
+ idx = np.flatnonzero(elements == el)
29
+ n = len(idx)
30
+ sorted_reps[:, start : start + n] = np.sort(
31
+ pos_reps[:, idx, axis],
32
+ axis=1,
33
+ )
34
+ start += n
35
+ return sorted_reps
36
+
37
+ matched = np.ones(
38
+ (len(pos_reps1), len(pos_reps2)),
39
+ dtype=bool,
40
+ )
41
+ for axis in range(3):
42
+ rows, cols = np.where(matched)
43
+ if len(rows) == 0:
44
+ break
45
+
46
+ t_start_s = time.time()
47
+ pos1_axis = sort_positions_axis(pos_reps1, elements1, axis)
48
+ pos2_axis = sort_positions_axis(pos_reps2, elements2, axis)
49
+ if verbose:
50
+ print(f"code: sort_match_screen (axis {axis})")
51
+ print(f" Sorting: {round((time.time() - t_start_s) * 1000, 2)} ms")
52
+
53
+ t_start_m = time.time()
54
+ diff = pos1_axis[rows] - pos2_axis[cols]
55
+ diff -= axis_abc[axis] * np.round(diff / axis_abc[axis])
56
+ matched[rows, cols] = np.all(np.abs(diff) <= pos_tol, axis=1)
57
+ if verbose:
58
+ print(f" Matching: {round((time.time() - t_start_m) * 1000, 2)} ms")
59
+ print(f" Candidate matches: {np.count_nonzero(matched)}")
60
+ return matched
61
+
62
+
63
+ def greedy_match(
64
+ pos1: np.ndarray,
65
+ pos2: np.ndarray,
66
+ elements1: np.ndarray,
67
+ elements2: np.ndarray,
68
+ axis_abc: np.ndarray,
69
+ pos_tol: float,
70
+ verbose: bool = False,
71
+ ):
72
+ judge = True
73
+ inv_axis_abc = 1 / axis_abc
74
+
75
+ t_start_g = time.time()
76
+ for el in np.unique(elements1):
77
+ if not judge:
78
+ break
79
+ targets1 = np.where(elements1 == el)[0]
80
+ targets2 = np.where(elements2 == el)[0]
81
+ used = np.zeros(len(targets1), dtype=bool)
82
+ for i in targets1:
83
+ diff = np.abs(pos2[targets2] - pos1[i])
84
+ diff -= axis_abc * np.round(inv_axis_abc * diff)
85
+ diff_max = np.max(np.abs(diff), axis=1)
86
+ diff[used] = np.inf
87
+ j = np.argmin(diff_max)
88
+ if diff_max[j] >= pos_tol:
89
+ judge = False
90
+ break
91
+ used[j] = True
92
+ if verbose:
93
+ print("code: greedy_match")
94
+ print(f" Matching: {round((time.time() - t_start_g) * 1000, 2)} ms")
95
+
96
+ if judge:
97
+ return True
98
+ else:
99
+ return False
100
+
101
+
102
+ def bottleneck_assignment(
103
+ pos1: np.ndarray,
104
+ pos2: np.ndarray,
105
+ elements1: np.ndarray,
106
+ elements2: np.ndarray,
107
+ axis_abc: np.ndarray,
108
+ pos_tol: float,
109
+ best_matching: bool = False,
110
+ verbose: bool = False,
111
+ ):
112
+ inv_axis_abc = 1 / axis_abc
113
+ for el in np.unique(elements1):
114
+ targets1 = np.where(elements1 == el)[0]
115
+ targets2 = np.where(elements2 == el)[0]
116
+ pos1_sub = pos1[targets1]
117
+ pos2_sub = pos2[targets2]
118
+
119
+ t_start_d = time.time()
120
+ diff = pos1_sub[:, None, :] - pos2_sub[None, :, :]
121
+ diff -= axis_abc * np.round(diff * inv_axis_abc)
122
+ pos_d = np.max(np.abs(diff), axis=2)
123
+ n = pos_d.shape[0]
124
+ if verbose:
125
+ print("code: bottleneck_assignment")
126
+ print(
127
+ f" Difference Calc.: {round((time.time() - t_start_d) * 1000, 2)} ms"
128
+ )
129
+
130
+ t_start_m = time.time()
131
+ if best_matching:
132
+ # Sort distinct candidate distances
133
+ values = np.unique(pos_d)
134
+ left, right = 0, len(values) - 1
135
+ while left < right:
136
+ mid = (left + right) // 2
137
+ rows, cols = np.where(pos_d <= values[mid])
138
+ graph = csr_array(
139
+ (
140
+ np.ones(len(rows), dtype=np.int8),
141
+ (rows, cols),
142
+ ),
143
+ shape=pos_d.shape,
144
+ )
145
+ matching = maximum_bipartite_matching(graph, perm_type="column")
146
+ if np.all(matching >= 0):
147
+ right = mid
148
+ else:
149
+ left = mid + 1
150
+
151
+ threshold = values[left]
152
+ rows, cols = np.where(pos_d <= threshold)
153
+ graph = csr_array(
154
+ (
155
+ np.ones(len(rows), dtype=np.int8),
156
+ (rows, cols),
157
+ ),
158
+ shape=pos_d.shape,
159
+ )
160
+ matching = maximum_bipartite_matching(graph, perm_type="column")
161
+ pos_max_diffs = pos_d[np.arange(n), matching].tolist()
162
+ if verbose:
163
+ print(f" Matching: {round((time.time() - t_start_m) * 1000, 2)} ms")
164
+ if max(pos_max_diffs) > pos_tol:
165
+ return False
166
+ else:
167
+ t_start_m = time.time()
168
+ rows, cols = np.where(pos_d <= pos_tol)
169
+ graph = csr_array(
170
+ (
171
+ np.ones(len(rows), dtype=np.int8),
172
+ (rows, cols),
173
+ ),
174
+ shape=pos_d.shape,
175
+ )
176
+ matching = maximum_bipartite_matching(graph, perm_type="column")
177
+ if verbose:
178
+ print(f" Matching: {round((time.time() - t_start_m) * 1000, 2)} ms")
179
+ if not np.all(matching >= 0):
180
+ return False
181
+ return True