From a812f20bf4dddfbd88c925262775ec2e45fd3909 Mon Sep 17 00:00:00 2001
From: Austin Noto-Moniz <anoto-moniz@citrine.io>
Date: Wed, 18 Sep 2024 10:45:17 -0400
Subject: [PATCH 1/4] Adjust type of ChemicalFormulaFeaturizer.powers.

The backend is opening up the type of powers to be floats. As such, this
commit will deserialize powers as a list of floats. However, to preserve
backwards compatability, the type of powers will be preserved, and a
new field (powers_float) with the list as floats is introduced. In
v4.0.0, the type of 'powers' will be changed, and 'powers_float' will be
deprecated before its removal in v5.0.0.
---
 src/citrine/__version__.py                    |  2 +-
 .../predictors/chemical_formula_featurizer.py | 23 +++++++++++++++++--
 tests/informatics/test_predictors.py          |  5 +++-
 3 files changed, 26 insertions(+), 4 deletions(-)

diff --git a/src/citrine/__version__.py b/src/citrine/__version__.py
index 7a829927f..85197cb4a 100644
--- a/src/citrine/__version__.py
+++ b/src/citrine/__version__.py
@@ -1 +1 @@
-__version__ = "3.5.4"
+__version__ = "3.6.0"
diff --git a/src/citrine/informatics/predictors/chemical_formula_featurizer.py b/src/citrine/informatics/predictors/chemical_formula_featurizer.py
index 76273fcbc..712b893b4 100644
--- a/src/citrine/informatics/predictors/chemical_formula_featurizer.py
+++ b/src/citrine/informatics/predictors/chemical_formula_featurizer.py
@@ -1,4 +1,5 @@
-from typing import List, Optional
+from typing import List, Optional, Union
+from warnings import warn
 
 from citrine._rest.resource import Resource
 from citrine._serialization import properties
@@ -133,7 +134,7 @@ class ChemicalFormulaFeaturizer(Resource["ChemicalFormulaFeaturizer"], Predictor
     input_descriptor = properties.Object(ChemicalFormulaDescriptor, 'input')
     features = properties.List(properties.String, 'features')
     excludes = properties.List(properties.String, 'excludes', default=[])
-    powers = properties.List(properties.Integer, 'powers')
+    _powers = properties.List(properties.Float, 'powers')
 
     typ = properties.String('type', default='ChemicalFormulaFeaturizer', deserializable=False)
 
@@ -152,5 +153,23 @@ def __init__(self,
         self.excludes = excludes if excludes is not None else []
         self.powers = powers if powers is not None else [1]
 
+    @property
+    def powers(self) -> List[int]:
+        """The list of powers when computing generalized weighted means of element properties."""
+        warn("The type of 'powers' will change to a list of floats in v4.0.0. To retrieve them as "
+             "floats now, use 'powers_float'.")
+        return [int(p) for p in self._powers]
+
+    @powers.setter
+    def powers(self, value: List[Union[int, float]]):
+        self._powers = value
+
+    @property
+    def powers_float(self) -> List[float]:
+        """Powers when computing generalized weighted means of element properties."""
+        warn("'powers_float' will be deprecated in v4.0.0 for 'powers', and removed in v5.0.0",
+             PendingDeprecationWarning)
+        return self._powers
+
     def __str__(self):
         return '<ChemicalFormulaFeaturizer {!r}>'.format(self.name)
diff --git a/tests/informatics/test_predictors.py b/tests/informatics/test_predictors.py
index 392438163..5c239f8d7 100644
--- a/tests/informatics/test_predictors.py
+++ b/tests/informatics/test_predictors.py
@@ -259,7 +259,10 @@ def test_chemical_featurizer(chemical_featurizer):
     assert chemical_featurizer.input_descriptor == ChemicalFormulaDescriptor("formula")
     assert chemical_featurizer.features == ["standard"]
     assert chemical_featurizer.excludes == []
-    assert chemical_featurizer.powers == [1, 2]
+    with pytest.warns(UserWarning):
+        assert chemical_featurizer.powers == [1, 2]
+    with pytest.warns(PendingDeprecationWarning):
+        assert chemical_featurizer.powers_float == [1.0, 2.0]
 
     assert str(chemical_featurizer) == "<ChemicalFormulaFeaturizer 'Chemical featurizer'>"
 

From 7cede2d1116fade6b4c7afa5d56de9e4b176a1b1 Mon Sep 17 00:00:00 2001
From: Austin Noto-Moniz <anoto-moniz@citrine.io>
Date: Wed, 18 Sep 2024 13:28:19 -0400
Subject: [PATCH 2/4] PR changes

---
 .../informatics/predictors/chemical_formula_featurizer.py | 6 +++---
 tests/informatics/test_predictors.py                      | 8 +++++++-
 2 files changed, 10 insertions(+), 4 deletions(-)

diff --git a/src/citrine/informatics/predictors/chemical_formula_featurizer.py b/src/citrine/informatics/predictors/chemical_formula_featurizer.py
index 712b893b4..7d867bd69 100644
--- a/src/citrine/informatics/predictors/chemical_formula_featurizer.py
+++ b/src/citrine/informatics/predictors/chemical_formula_featurizer.py
@@ -157,7 +157,7 @@ def __init__(self,
     def powers(self) -> List[int]:
         """The list of powers when computing generalized weighted means of element properties."""
         warn("The type of 'powers' will change to a list of floats in v4.0.0. To retrieve them as "
-             "floats now, use 'powers_float'.")
+             "floats now, use 'powers_as_float'.")
         return [int(p) for p in self._powers]
 
     @powers.setter
@@ -165,9 +165,9 @@ def powers(self, value: List[Union[int, float]]):
         self._powers = value
 
     @property
-    def powers_float(self) -> List[float]:
+    def powers_as_float(self) -> List[float]:
         """Powers when computing generalized weighted means of element properties."""
-        warn("'powers_float' will be deprecated in v4.0.0 for 'powers', and removed in v5.0.0",
+        warn("'powers_as_float' will be deprecated in v4.0.0 for 'powers', and removed in v5.0.0",
              PendingDeprecationWarning)
         return self._powers
 
diff --git a/tests/informatics/test_predictors.py b/tests/informatics/test_predictors.py
index 5c239f8d7..234c7b202 100644
--- a/tests/informatics/test_predictors.py
+++ b/tests/informatics/test_predictors.py
@@ -262,7 +262,7 @@ def test_chemical_featurizer(chemical_featurizer):
     with pytest.warns(UserWarning):
         assert chemical_featurizer.powers == [1, 2]
     with pytest.warns(PendingDeprecationWarning):
-        assert chemical_featurizer.powers_float == [1.0, 2.0]
+        assert chemical_featurizer.powers_as_float == [1.0, 2.0]
 
     assert str(chemical_featurizer) == "<ChemicalFormulaFeaturizer 'Chemical featurizer'>"
 
@@ -275,6 +275,12 @@ def test_chemical_featurizer(chemical_featurizer):
         'powers': [1, 2],
         'type': 'ChemicalFormulaFeaturizer'
     }
+    
+    chemical_featurizer.powers = [0.5, -1.5]
+    with pytest.warns(PendingDeprecationWarning):
+        assert chemical_featurizer.powers_as_float == [0.5, -1.5]
+    with pytest.warns(UserWarning):
+        assert chemical_featurizer.powers == [0, -1]
 
 
 def test_auto_ml(auto_ml):

From 3a9a554e074dab3e06e9131bd0feb903cb09fcb3 Mon Sep 17 00:00:00 2001
From: Austin Noto-Moniz <anoto-moniz@citrine.io>
Date: Wed, 18 Sep 2024 14:12:44 -0400
Subject: [PATCH 3/4] Update test

---
 tests/informatics/test_predictors.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/tests/informatics/test_predictors.py b/tests/informatics/test_predictors.py
index 234c7b202..0645fe4a7 100644
--- a/tests/informatics/test_predictors.py
+++ b/tests/informatics/test_predictors.py
@@ -276,9 +276,9 @@ def test_chemical_featurizer(chemical_featurizer):
         'type': 'ChemicalFormulaFeaturizer'
     }
     
-    chemical_featurizer.powers = [0.5, -1.5]
+    chemical_featurizer.powers = [0.5, -1]
     with pytest.warns(PendingDeprecationWarning):
-        assert chemical_featurizer.powers_as_float == [0.5, -1.5]
+        assert chemical_featurizer.powers_as_float == [0.5, -1.0]
     with pytest.warns(UserWarning):
         assert chemical_featurizer.powers == [0, -1]
 

From 497ba9fbbf5938e3820c004d27b45e27cd8160b7 Mon Sep 17 00:00:00 2001
From: Austin Noto-Moniz <anoto-moniz@citrine.io>
Date: Thu, 19 Sep 2024 09:19:26 -0400
Subject: [PATCH 4/4] Add warning when truncation occurs.

---
 .../informatics/predictors/chemical_formula_featurizer.py   | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/src/citrine/informatics/predictors/chemical_formula_featurizer.py b/src/citrine/informatics/predictors/chemical_formula_featurizer.py
index 7d867bd69..45213ab17 100644
--- a/src/citrine/informatics/predictors/chemical_formula_featurizer.py
+++ b/src/citrine/informatics/predictors/chemical_formula_featurizer.py
@@ -158,7 +158,11 @@ def powers(self) -> List[int]:
         """The list of powers when computing generalized weighted means of element properties."""
         warn("The type of 'powers' will change to a list of floats in v4.0.0. To retrieve them as "
              "floats now, use 'powers_as_float'.")
-        return [int(p) for p in self._powers]
+        truncated = [int(p) for p in self._powers]
+        if truncated != self._powers:
+            diffs = [f"{x} => {y}" for x, y in zip(self._powers, truncated) if x != y]
+            warn(f"The following powers were cast to ints: {'; '.join(diffs)}.")
+        return truncated
 
     @powers.setter
     def powers(self, value: List[Union[int, float]]):