Skip to content

Commit d34a414

Browse files
authored
Merge pull request #167 from stevenhua0320/deprecate-symmetryutilities-3
chore: deprecate method in symmetryutilities
2 parents 567f7fa + f93a859 commit d34a414

File tree

3 files changed

+182
-5
lines changed

3 files changed

+182
-5
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
**Added:**
2+
3+
* Added ``equal_positions`` method in ``symmetryutilities.py``
4+
* Added ``expand_position`` method in ``symmetryutilities.py``
5+
* Added ``null_space`` method in ``symmetryutilities.py``
6+
7+
**Changed:**
8+
9+
* <news item>
10+
11+
**Deprecated:**
12+
13+
* Deprecated ``equalPositions`` method in ``symmetryutilities.py`` for removal in version 4.0.0
14+
* Deprecated ``expandPosition`` method in ``symmetryutilities.py`` for removal in version 4.0.0
15+
* Deprecated ``nullSpace`` method in ``symmetryutilities.py`` for removal in version 4.0.0
16+
17+
**Removed:**
18+
19+
* <news item>
20+
21+
**Fixed:**
22+
23+
* <news item>
24+
25+
**Security:**
26+
27+
* <news item>

src/diffpy/structure/symmetryutilities.py

Lines changed: 53 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,24 @@
6060
"nearest_site_index",
6161
removal_version,
6262
)
63+
equalPositions_deprecation_msg = build_deprecation_message(
64+
base,
65+
"equalPositions",
66+
"equal_positions",
67+
removal_version,
68+
)
69+
expandPosition_deprecation_msg = build_deprecation_message(
70+
base,
71+
"expandPosition",
72+
"expand_position",
73+
removal_version,
74+
)
75+
nullSpace_deprecation_msg = build_deprecation_message(
76+
base,
77+
"nullSpace",
78+
"null_space",
79+
removal_version,
80+
)
6381

6482
# Constants ------------------------------------------------------------------
6583

@@ -299,7 +317,17 @@ def nearest_site_index(sites, xyz):
299317
return nearindex
300318

301319

320+
@deprecated(equalPositions_deprecation_msg)
302321
def equalPositions(xyz0, xyz1, eps):
322+
"""'diffpy.structure.equalPositions' is deprecated and will be
323+
removed in version 4.0.0.
324+
325+
Please use 'diffpy.structure.equal_positions' instead.
326+
"""
327+
return equal_positions(xyz0, xyz1, eps)
328+
329+
330+
def equal_positions(xyz0, xyz1, eps):
303331
"""Equality of two coordinates with optional tolerance.
304332
305333
Parameters
@@ -319,7 +347,17 @@ def equalPositions(xyz0, xyz1, eps):
319347
return numpy.all(dxyz <= eps)
320348

321349

350+
@deprecated(expandPosition_deprecation_msg)
322351
def expandPosition(spacegroup, xyz, sgoffset=[0, 0, 0], eps=None):
352+
"""'diffpy.structure.expandPosition' is deprecated and will be
353+
removed in version 4.0.0.
354+
355+
Please use 'diffpy.structure.expand_position' instead.
356+
"""
357+
return expand_position(spacegroup, xyz, sgoffset, eps)
358+
359+
360+
def expand_position(spacegroup, xyz, sgoffset=[0, 0, 0], eps=None):
323361
"""Obtain unique equivalent positions and corresponding operations.
324362
325363
Parameters
@@ -358,7 +396,7 @@ def expandPosition(spacegroup, xyz, sgoffset=[0, 0, 0], eps=None):
358396
if positions:
359397
nearpos = positions[nearest_site_index(positions, pos)]
360398
# is it an equivalent position?
361-
if equalPositions(nearpos, pos, eps):
399+
if equal_positions(nearpos, pos, eps):
362400
# tpl should map to the same list as nearpos
363401
site_symops[tpl] = site_symops[pos2tuple(nearpos)]
364402
pos_is_new = False
@@ -372,7 +410,17 @@ def expandPosition(spacegroup, xyz, sgoffset=[0, 0, 0], eps=None):
372410
return positions, pos_symops, multiplicity
373411

374412

413+
@deprecated(nullSpace_deprecation_msg)
375414
def nullSpace(A):
415+
"""'diffpy.structure.nullSpace' is deprecated and will be removed in
416+
version 4.0.0.
417+
418+
Please use 'diffpy.structure.null_space' instead.
419+
"""
420+
return null_space(A)
421+
422+
423+
def null_space(A):
376424
"""Null space of matrix A."""
377425
from numpy import linalg
378426

@@ -588,7 +636,7 @@ def _findNullSpace(self):
588636
R0 = self.invariants[0].R
589637
Rdiff = [(symop.R - R0) for symop in self.invariants]
590638
Rdiff = numpy.concatenate(Rdiff, axis=0)
591-
self.null_space = nullSpace(Rdiff)
639+
self.null_space = null_space(Rdiff)
592640
if self.null_space.size == 0:
593641
return
594642
# reverse sort rows of null_space rows by absolute value
@@ -649,7 +697,7 @@ def _findUSpace(self):
649697
Ucj2 = numpy.dot(R, numpy.dot(Ucj, R.T))
650698
for i, kl in i6kl:
651699
R6z[i, j] += Ucj2[kl]
652-
Usp6 = nullSpace(R6zall)
700+
Usp6 = null_space(R6zall)
653701
# normalize Usp6 by its maximum component
654702
mxcols = numpy.argmax(numpy.fabs(Usp6), axis=1)
655703
mxrows = numpy.arange(len(mxcols))
@@ -716,7 +764,7 @@ def positionFormula(self, pos, xyzsymbols=("x", "y", "z")):
716764
# find pos in eqxyz
717765
idx = nearest_site_index(self.eqxyz, pos)
718766
eqpos = self.eqxyz[idx]
719-
if not equalPositions(eqpos, pos, self.eps):
767+
if not equal_positions(eqpos, pos, self.eps):
720768
return {}
721769
# any rotation matrix should do fine
722770
R = self.symops[idx][0].R
@@ -768,7 +816,7 @@ def UFormula(self, pos, Usymbols=stdUsymbols):
768816
# find pos in eqxyz
769817
idx = nearest_site_index(self.eqxyz, pos)
770818
eqpos = self.eqxyz[idx]
771-
if not equalPositions(eqpos, pos, self.eps):
819+
if not equal_positions(eqpos, pos, self.eps):
772820
return {}
773821
# any rotation matrix should do fine
774822
R = self.symops[idx][0].R

tests/test_symmetryutilities.py

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,18 @@
2727
GeneratorSite,
2828
SymmetryConstraints,
2929
_Position2Tuple,
30+
equal_positions,
31+
equalPositions,
32+
expand_position,
3033
expandPosition,
3134
is_constant_formula,
3235
is_space_group_latt_parms,
3336
isconstantFormula,
3437
isSpaceGroupLatPar,
3538
nearest_site_index,
3639
nearestSiteIndex,
40+
null_space,
41+
nullSpace,
3742
position_difference,
3843
positionDifference,
3944
pruneFormulaDictionary,
@@ -127,6 +132,17 @@ def test_expandPosition(self):
127132
self.assertEqual(4, pmult)
128133
return
129134

135+
def test_expand_position(self):
136+
"""Check expand_position()"""
137+
# ok again Ni example
138+
fcc = GetSpaceGroup(225)
139+
pos, pops, pmult = expand_position(fcc, [0, 0, 0])
140+
self.assertTrue(numpy.all(pos[0] == 0.0))
141+
self.assertEqual(4, len(pos))
142+
self.assertEqual(192, sum([len(line) for line in pops]))
143+
self.assertEqual(4, pmult)
144+
return
145+
130146
def test_pruneFormulaDictionary(self):
131147
"""Check pruneFormulaDictionary()"""
132148
fmdict = {"x": "3*y-0.17", "y": "0", "z": "0.13"}
@@ -152,6 +168,12 @@ def test_is_constant_formula(self):
152168
self.assertTrue(is_constant_formula("+13/ 9"))
153169
return
154170

171+
def test_equalPositions(self):
172+
"""Check equalPositions()"""
173+
self.assertTrue(equalPositions([0.1, 0.2, 0.3], [0.1, 0.2, 0.3], 1.0e-5))
174+
self.assertTrue(equalPositions([0.1 + 0.5e-5, 0.2 + 0.5e-5, 0.3 + 0.5e-5], [0.1, 0.2, 0.3], 1.0e-5))
175+
self.assertFalse(equalPositions([0.2, 0.2, 0.3], [0.1, 0.2, 0.3], 1.0e-5))
176+
155177

156178
# End of class TestRoutines
157179

@@ -704,5 +726,85 @@ def test_nearest_site_index(sites, xyz, expected):
704726
assert actual == expected
705727

706728

729+
@pytest.mark.parametrize(
730+
"xyz0, xyz1, eps, expected",
731+
[
732+
pytest.param([0.1, 0.2, 0.3], [0.1, 0.2, 0.3], 1.0e-5, True), # C1: same position
733+
pytest.param(
734+
[0.1 + 0.5e-5, 0.2 + 0.5e-5, 0.3 + 0.5e-5], [0.1, 0.2, 0.3], 1.0e-5, True
735+
), # C2: same position with some tolerance
736+
pytest.param([0.2, 0.2, 0.3], [0.1, 0.2, 0.3], 1.0e-5, False), # C3: different positions
737+
],
738+
)
739+
def test_equal_positions(xyz0, xyz1, eps, expected):
740+
"""Check equalPositions."""
741+
actual = equal_positions(xyz0, xyz1, eps)
742+
assert actual == expected
743+
744+
745+
@pytest.mark.parametrize(
746+
"A, expected_dim",
747+
[
748+
pytest.param( # C1: full-rank 2x2 matrix
749+
[[1.0, 0.0], [0.0, 1.0]],
750+
0,
751+
),
752+
pytest.param( # C2: Nullspace has dim 1
753+
[[1.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 2.0]],
754+
1,
755+
),
756+
pytest.param( # C3: Nullspace has dim 2
757+
[[1.0, 2.0, 3.0], [2.0, 4.0, 6.0], [0.0, 0.0, 0.0]],
758+
2,
759+
),
760+
pytest.param( # C4: Nullspace has dim 2
761+
[[0.0, 0.0], [0.0, 0.0]],
762+
2,
763+
),
764+
],
765+
)
766+
def test_nullSpace(A, expected_dim):
767+
"""Check nullSpace returns an orthonormal basis on supported square
768+
matrices."""
769+
A = numpy.asarray(A, dtype=float)
770+
actual = nullSpace(A)
771+
772+
assert actual.shape == (expected_dim, A.shape[1])
773+
assert numpy.allclose(A @ actual.T, numpy.zeros((A.shape[0], expected_dim)), atol=1e-12)
774+
assert numpy.allclose(actual @ actual.T, numpy.eye(expected_dim), atol=1e-12)
775+
776+
777+
@pytest.mark.parametrize(
778+
"A, expected_dim",
779+
[
780+
pytest.param( # C1: full-rank 2x2 matrix
781+
[[1.0, 0.0], [0.0, 1.0]],
782+
0,
783+
),
784+
pytest.param( # C2: Nullspace has dim 1
785+
[[1.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 2.0]],
786+
1,
787+
),
788+
pytest.param( # C3: Nullspace has dim 2
789+
[[1.0, 2.0, 3.0], [2.0, 4.0, 6.0], [0.0, 0.0, 0.0]],
790+
2,
791+
),
792+
pytest.param( # C4: Nullspace has dim 2
793+
[[0.0, 0.0], [0.0, 0.0]],
794+
2,
795+
),
796+
],
797+
)
798+
def test_null_space(A, expected_dim):
799+
"""Check null_space returns an orthonormal basis on supported square
800+
matrices."""
801+
A = numpy.asarray(A, dtype=float)
802+
actual = null_space(A)
803+
804+
assert actual.shape == (expected_dim, A.shape[1])
805+
assert numpy.allclose(A @ actual.T, numpy.zeros((A.shape[0], expected_dim)), atol=1e-12)
806+
assert numpy.allclose(actual @ actual.T, numpy.eye(expected_dim), atol=1e-12)
807+
808+
707809
if __name__ == "__main__":
708810
unittest.main()

0 commit comments

Comments
 (0)