rsspolymlp 0.4.2__tar.gz → 0.4.4__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.4}/PKG-INFO +19 -20
  2. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/README.md +18 -19
  3. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/pyproject.toml +1 -1
  4. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/analysis/rss_summarize.py +31 -14
  5. rsspolymlp-0.4.4/src/rsspolymlp/analysis/struct_matcher/assignment_algo.py +181 -0
  6. rsspolymlp-0.4.4/src/rsspolymlp/analysis/struct_matcher/gen_struct_rep.py +371 -0
  7. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/analysis/struct_matcher/identify_uniqstr.py +88 -39
  8. rsspolymlp-0.4.4/src/rsspolymlp/analysis/struct_matcher/redrep_lattice.py +144 -0
  9. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/analysis/struct_matcher/redrep_position.py +174 -115
  10. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/analysis/struct_matcher/struct_match.py +25 -10
  11. rsspolymlp-0.4.4/src/rsspolymlp/analysis/struct_matcher/test/gen_lattice.py +53 -0
  12. rsspolymlp-0.4.4/src/rsspolymlp/analysis/struct_matcher/test/time_measure.py +0 -0
  13. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/api/cli_rsspolymlp.py +8 -0
  14. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/api/cli_rsspolymlp_utils.py +35 -29
  15. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/api/rsspolymlp.py +3 -0
  16. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/api/rsspolymlp_utils.py +70 -40
  17. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/common/atomic_energy.py +2 -0
  18. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/common/property.py +1 -1
  19. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/mlp_dev/dataset/split_dataset.py +4 -3
  20. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/rss/eliminate_duplicates.py +5 -4
  21. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/utils/matplot_util/make_plot.py +14 -3
  22. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/utils/spglib_utils.py +5 -1
  23. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4/src/rsspolymlp.egg-info}/PKG-INFO +19 -20
  24. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp.egg-info/SOURCES.txt +2 -0
  25. rsspolymlp-0.4.2/src/rsspolymlp/analysis/struct_matcher/assignment_algo.py +0 -112
  26. rsspolymlp-0.4.2/src/rsspolymlp/analysis/struct_matcher/gen_struct_rep.py +0 -325
  27. rsspolymlp-0.4.2/src/rsspolymlp/analysis/struct_matcher/redrep_lattice.py +0 -541
  28. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/LICENSE +0 -0
  29. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/setup.cfg +0 -0
  30. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/__init__.py +0 -0
  31. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/analysis/__init__.py +0 -0
  32. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/analysis/eos.py +0 -0
  33. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/analysis/ghost_minima.py +0 -0
  34. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/analysis/phase_analysis.py +0 -0
  35. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/analysis/struct_matcher/__init__.py +0 -0
  36. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/analysis/struct_matcher/chiral_spg.py +0 -0
  37. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/api/__init__.py +0 -0
  38. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/api/cli_rsspolymlp_devkit.py +0 -0
  39. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/api/cli_rsspolymlp_plot.py +0 -0
  40. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/api/rsspolymlp_devkit.py +0 -0
  41. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/api/rsspolymlp_plot.py +0 -0
  42. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/common/__init__.py +0 -0
  43. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/common/composition.py +0 -0
  44. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/common/convert_dict.py +0 -0
  45. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/common/interface_vasp.py +0 -0
  46. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/mlp_dev/dataset/compress_vasprun.py +0 -0
  47. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/mlp_dev/dataset/gen_mlp_dataset.py +0 -0
  48. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/mlp_dev/estimate_cost.py +0 -0
  49. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/mlp_dev/model_selection/binary/gen_single_model.py +0 -0
  50. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/mlp_dev/model_selection/elemental/gen_hybrid_model.py +0 -0
  51. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/mlp_dev/model_selection/elemental/gen_single_model.py +0 -0
  52. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/mlp_dev/model_selection/n_feature.py +0 -0
  53. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/mlp_dev/model_selection/pypolymlp_gridsearch.py +0 -0
  54. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/mlp_dev/model_selection/reduce_grid.py +0 -0
  55. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/mlp_dev/pareto_opt_mlp.py +0 -0
  56. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/mlp_dev/polymlp_dev.py +0 -0
  57. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/rss/__init__.py +0 -0
  58. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/rss/load_logfile.py +0 -0
  59. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/rss/optimization_mlp.py +0 -0
  60. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/rss/random_struct.py +0 -0
  61. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/utils/__init__.py +0 -0
  62. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/utils/lammps_utils.py +0 -0
  63. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/utils/matplot_util/__init__.py +0 -0
  64. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/utils/matplot_util/custom_plt.py +0 -0
  65. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/utils/matplot_util/examples/__init__.py +0 -0
  66. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/utils/matplot_util/examples/example.py +0 -0
  67. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/utils/matplot_util/examples/template.py +0 -0
  68. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/utils/opt_sscha/opt_geometry_sscha.py +0 -0
  69. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/utils/opt_sscha/opt_position_sscha.py +0 -0
  70. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/utils/opt_sscha/run_opt_sscha.py +0 -0
  71. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/utils/opt_sscha/sscha_property.py +0 -0
  72. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/utils/pymatgen_utils.py +0 -0
  73. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/utils/vasp_util/__init__.py +0 -0
  74. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/utils/vasp_util/api.py +0 -0
  75. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/utils/vasp_util/gen_incar.py +0 -0
  76. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/utils/vasp_util/gen_script.py +0 -0
  77. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp/utils/vasp_util/write_poscar.py +0 -0
  78. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp.egg-info/dependency_links.txt +0 -0
  79. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp.egg-info/entry_points.txt +0 -0
  80. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp.egg-info/requires.txt +0 -0
  81. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/src/rsspolymlp.egg-info/top_level.txt +0 -0
  82. {rsspolymlp-0.4.2 → rsspolymlp-0.4.4}/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.4
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,21 @@ 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
+ - Python API
83
+ - [RSS workflow](docs/api_rsspolymlp.md)
84
+ - [VASP calculation utilities](src/rsspolymlp/utils/vasp_util/readme.md)
85
+ - Single-point calculation
86
+ - Local geometry optimization
87
+ - [Matplotlib utilities](src/rsspolymlp/utils/matplot_util/readme.md)
88
+
89
+
91
90
  ## Citation of rsspolymlp
92
91
 
93
92
  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,21 @@ 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
+ - Python API
52
+ - [RSS workflow](docs/api_rsspolymlp.md)
53
+ - [VASP calculation utilities](src/rsspolymlp/utils/vasp_util/readme.md)
54
+ - Single-point calculation
55
+ - Local geometry optimization
56
+ - [Matplotlib utilities](src/rsspolymlp/utils/matplot_util/readme.md)
57
+
58
+
60
59
  ## Citation of rsspolymlp
61
60
 
62
61
  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.4"
8
8
  description = "A framework for random structure search using polynomial MLPs"
9
9
  license = "MIT"
10
10
  authors = [
@@ -6,15 +6,15 @@ import xml.etree.ElementTree as ET
6
6
  from collections import defaultdict
7
7
  from pathlib import Path
8
8
  from time import time
9
- from typing import Optional
9
+ from typing import Literal, Optional
10
10
 
11
11
  import numpy as np
12
12
 
13
13
  from pypolymlp.utils.vasp_utils import write_poscar_file
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
20
  UniqueStructIdentifier,
@@ -38,6 +38,7 @@ class RSSResultSummarizer:
38
38
  element_order: list = None,
39
39
  num_process: int = -1,
40
40
  backend: str = "loky",
41
+ assign_method: Literal["redrep", "greedy", "HK", "pymatgen"] = "redrep",
41
42
  primitive_symprecs: list[float] = [1e-5, 1e-4, 1e-3, 1e-2],
42
43
  output_poscar: bool = False,
43
44
  thresholds: list[float] = None,
@@ -51,6 +52,7 @@ class RSSResultSummarizer:
51
52
  self.element_order = element_order
52
53
  self.num_process = num_process
53
54
  self.backend = backend
55
+ self.assign_method = assign_method
54
56
  self.primitive_symprecs = primitive_symprecs
55
57
  self.output_poscar = output_poscar
56
58
  self.thresholds = thresholds
@@ -58,7 +60,7 @@ class RSSResultSummarizer:
58
60
  self.update_parent = update_parent
59
61
 
60
62
  self.pressure = None
61
- self.analyzer = UniqueStructIdentifier()
63
+ self.analyzer = None
62
64
 
63
65
  def run_summarize(self):
64
66
  os.makedirs("json", exist_ok=True)
@@ -90,10 +92,13 @@ class RSSResultSummarizer:
90
92
  axis_tol = 0.1
91
93
  pos_tol = 0.1
92
94
 
95
+ self.analyzer = UniqueStructIdentifier(
96
+ axis_tol=axis_tol,
97
+ pos_tol=pos_tol,
98
+ assign_method=self.assign_method,
99
+ )
93
100
  for comp_tag, res_paths in json_paths_c.items():
94
101
  print(f"Composition {comp_tag}: summarizing...")
95
- self.analyzer = UniqueStructIdentifier()
96
-
97
102
  time_start = time()
98
103
 
99
104
  processed_paths = set()
@@ -119,6 +124,7 @@ class RSSResultSummarizer:
119
124
  rss_results_c[comp_tag][res_path],
120
125
  axis_tol=axis_tol,
121
126
  pos_tol=pos_tol,
127
+ assign_method=self.assign_method,
122
128
  pre_analyzer=self.analyzer,
123
129
  )
124
130
 
@@ -127,11 +133,11 @@ class RSSResultSummarizer:
127
133
  unique_structs = self.analyzer.unique_str
128
134
  unique_str_prop = self.analyzer.unique_str_prop
129
135
  enthalpies = np.array([s["energy"] for s in unique_str_prop])
130
- distances = np.array([s["least_distance"] for s in unique_str_prop])
131
136
  sort_idx = np.argsort(enthalpies)
132
137
 
133
138
  if not self.parse_vasp:
134
139
  os.makedirs("ghost_minima", exist_ok=True)
140
+ distances = np.array([s["least_distance"] for s in unique_str_prop])
135
141
  is_ghost_minima, ghost_minima_info = detect_ghost_minima(
136
142
  enthalpies[sort_idx], distances[sort_idx]
137
143
  )
@@ -196,8 +202,6 @@ class RSSResultSummarizer:
196
202
 
197
203
  for comp_tag, res_paths in json_paths_c.items():
198
204
  print(f"Composition {comp_tag}: summarizing...")
199
- self.analyzer = UniqueStructIdentifier()
200
-
201
205
  if not self.parse_vasp:
202
206
  axis_tol = 0.03
203
207
  pos_tol = 0.03
@@ -210,10 +214,11 @@ class RSSResultSummarizer:
210
214
  print(f" - Processing result file (parent): {res_path}")
211
215
  self.analyzer = identify_unique_structure(
212
216
  rss_results_c[comp_tag][res_path],
213
- standardize_axis=True,
217
+ normalize_axis=True,
214
218
  keep_unique=True,
215
219
  axis_tol=axis_tol,
216
220
  pos_tol=pos_tol,
221
+ assign_method=self.assign_method,
217
222
  pre_analyzer=self.analyzer,
218
223
  )
219
224
  time_finish = time() - time_start
@@ -225,7 +230,7 @@ class RSSResultSummarizer:
225
230
  sort_idx = np.argsort(enthalpies)
226
231
  sort_idx = sorted(
227
232
  sort_idx,
228
- key=lambda i: len(unique_str_sorted[i]),
233
+ key=lambda i: len(unique_structs[i]),
229
234
  reverse=True,
230
235
  )
231
236
  unique_str_sorted = [unique_structs[i] for i in sort_idx]
@@ -264,11 +269,13 @@ class RSSResultSummarizer:
264
269
  r["dupstr_paths"] = set(r["dupstr_paths"])
265
270
 
266
271
  print(" - Converting reduced crystal structure representation...")
267
- struct_reps_list = generate_struct_reps_parallel(
272
+ generator = StructRepGenerator(
273
+ primitive_symprecs=self.primitive_symprecs,
274
+ )
275
+ struct_reps_list = generator.generate_struct_reps_parallel(
268
276
  rss_results,
269
277
  num_process=self.num_process,
270
278
  backend=self.backend,
271
- symprec_set1=self.symprec_set,
272
279
  )
273
280
  if self.update_parent:
274
281
  print(" - Eliminating duplicate structures...")
@@ -589,6 +596,14 @@ def log_all_unique_structures(
589
596
  with open(file_name, "a") as f:
590
597
  print("unique_structures:", file=f)
591
598
  for idx1, _str in enumerate(unique_structs):
599
+ if unique_str_prop is not None:
600
+ sorted_data = sorted(
601
+ zip(unique_structs[idx1], unique_str_prop[idx1]),
602
+ key=lambda x: x[1]["energy"],
603
+ )
604
+ unique_structs[idx1], unique_str_prop[idx1] = map(
605
+ list, zip(*sorted_data)
606
+ )
592
607
  print(f" - struct_No: {idx1 + 1}", file=f)
593
608
  print(" structures:", file=f)
594
609
  for idx2, _str in enumerate(unique_structs[idx1]):
@@ -600,7 +615,9 @@ def log_all_unique_structures(
600
615
  objprop = PropUtil(polymlp_st.axis.T, polymlp_st.positions.T)
601
616
  print(f" - sub_struct_No: '{idx1 + 1}_{idx2 + 1}'", file=f)
602
617
  print(f" struct_path: {_str.struct_path}", file=f)
603
- print(f" pressure: {_str.pressure}", file=f)
618
+ print(
619
+ f" pressure: {unique_str_prop[idx1][idx2]['pressure']}", file=f
620
+ )
604
621
  print(f" enthalpy: {e}", file=f)
605
622
  print(f" axis: {objprop.abc}", file=f)
606
623
  print(
@@ -630,7 +647,7 @@ def log_all_unique_structures(
630
647
  if unique_other_prop is not None:
631
648
  print(" properties:", unique_other_prop[idx1], file=f)
632
649
 
633
- comp_res = compute_composition(unique_structs[0][0].original_structure.elements)
650
+ comp_res = compute_composition(unique_structs[0][0].original_polymlp_st.elements)
634
651
 
635
652
  rss_result_all = {
636
653
  "elements": comp_res.unique_elements.tolist(),
@@ -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