Repository: cogent3/cogent3
Pull request #1180 (2022-10-26 01:34:51)

Rebase Data store refactor


Patch:
diff --git a/src/cogent3/app/sample.py b/src/cogent3/app/sample.py
index b2bc82069..7ce1c3b2a 100644
--- a/src/cogent3/app/sample.py
+++ b/src/cogent3/app/sample.py
@@ -191,7 +191,7 @@ class take_codon_positions:
         self,
         *positions,
         fourfold_degenerate=False,
-        gc="Standard Nuclear",
+        gc="Standard",
         moltype="dna",
     ):
         """selects the indicated codon positions from an alignment
diff --git a/src/cogent3/core/alphabet.py b/src/cogent3/core/alphabet.py
index 340962b48..2fca6ec70 100644
--- a/src/cogent3/core/alphabet.py
+++ b/src/cogent3/core/alphabet.py
@@ -547,14 +547,9 @@ class Alphabet(Enumeration):
         Note that the result is not a JointEnumeration object, and cannot
         unpack its indices. However, the items in the result _are_ all strings.
         """
-        crossproduct = [""]
-        for a in range(word_length):
-            n = []
-            for c in crossproduct:
-                for m in self:
-                    n.append(m + c)
-            crossproduct = n
-        return Alphabet(crossproduct, moltype=self.moltype)
+        states = (list(self),) * word_length
+        cross_product = ["".join(combo) for combo in product(*states)]
+        return Alphabet(cross_product, moltype=self.moltype)
 
     def from_seq_to_array(self, sequence):
         """Returns an array of indices corresponding to items in sequence.
diff --git a/src/cogent3/core/genetic_code.py b/src/cogent3/core/genetic_code.py
index fd2e65c99..bf6e92026 100644
--- a/src/cogent3/core/genetic_code.py
+++ b/src/cogent3/core/genetic_code.py
@@ -353,7 +353,7 @@ NcbiGeneticCodeData = [
         [
             "FFLLSSSSYY**CC*WLLLLPPPPHHQQRRRRIIIMTTTTNNKKSSRRVVVVAAAADDEEGGGG",
             1,
-            "Standard Nuclear",
+            "Standard",
             "---M---------------M---------------M----------------------------",
         ],
         [
@@ -366,12 +366,13 @@ NcbiGeneticCodeData = [
             "FFLLSSSSYY**CCWWTTTTPPPPHHQQRRRRIIMMTTTTNNKKSSRRVVVVAAAADDEEGGGG",
             3,
             "Yeast Mitochondrial",
-            "----------------------------------MM----------------------------",
+            "----------------------------------MM---------------M------------",
         ],
         [
             "FFLLSSSSYY**CCWWLLLLPPPPHHQQRRRRIIIMTTTTNNKKSSRRVVVVAAAADDEEGGGG",
             4,
-            "Mold, Protozoan, and Coelenterate Mitochondrial, and Mycoplasma/Spiroplasma Nuclear",
+            "Mold Mitochondrial; Protozoan Mitochondrial; Coelenterate "
+            "Mitochondrial; Mycoplasma; Spiroplasma",
             "--MM---------------M------------MMMM---------------M------------",
         ],
         [
@@ -383,13 +384,13 @@ NcbiGeneticCodeData = [
         [
             "FFLLSSSSYYQQCC*WLLLLPPPPHHQQRRRRIIIMTTTTNNKKSSRRVVVVAAAADDEEGGGG",
             6,
-            "Ciliate, Dasycladacean and Hexamita Nuclear",
+            "Ciliate Nuclear; Dasycladacean Nuclear; Hexamita Nuclear",
             "-----------------------------------M----------------------------",
         ],
         [
             "FFLLSSSSYY**CCWWLLLLPPPPHHQQRRRRIIIMTTTTNNNKSSSSVVVVAAAADDEEGGGG",
             9,
-            "Echinoderm and Flatworm Mitochondrial",
+            "Echinoderm Mitochondrial; Flatworm Mitochondrial",
             "-----------------------------------M---------------M------------",
         ],
         [
@@ -401,7 +402,7 @@ NcbiGeneticCodeData = [
         [
             "FFLLSSSSYY**CC*WLLLLPPPPHHQQRRRRIIIMTTTTNNKKSSRRVVVVAAAADDEEGGGG",
             11,
-            "Bacterial Nuclear and Plant Plastid",
+            "Bacterial, Archaeal and Plant Plastid",
             "---M---------------M------------MMMM---------------M------------",
         ],
         [
@@ -414,7 +415,7 @@ NcbiGeneticCodeData = [
             "FFLLSSSSYY**CCWWLLLLPPPPHHQQRRRRIIMMTTTTNNKKSSGGVVVVAAAADDEEGGGG",
             13,
             "Ascidian Mitochondrial",
-            "-----------------------------------M----------------------------",
+            "---M------------------------------MM---------------M------------",
         ],
         [
             "FFLLSSSSYYY*CCWWLLLLPPPPHHQQRRRRIIIMTTTTNNNKSSSSVVVVAAAADDEEGGGG",
@@ -425,7 +426,7 @@ NcbiGeneticCodeData = [
         [
             "FFLLSSSSYY*QCC*WLLLLPPPPHHQQRRRRIIIMTTTTNNKKSSRRVVVVAAAADDEEGGGG",
             15,
-            "Blepharisma Nuclear",
+            "Blepharisma Macronuclear",
             "-----------------------------------M----------------------------",
         ],
         [
@@ -436,7 +437,7 @@ NcbiGeneticCodeData = [
         ],
         [
             "FFLLSSSSYY**CCWWLLLLPPPPHHQQRRRRIIMMTTTTNNNKSSSSVVVVAAAADDEEGGGG",
-            20,
+            21,
             "Trematode Mitochondrial",
             "-----------------------------------M---------------M------------",
         ],
@@ -450,6 +451,67 @@ NcbiGeneticCodeData = [
             "FF*LSSSSYY**CC*WLLLLPPPPHHQQRRRRIIIMTTTTNNKKSSRRVVVVAAAADDEEGGGG",
             23,
             "Thraustochytrium Mitochondrial",
+            "--------------------------------M--M---------------M------------",
+        ],
+        [
+            "FFLLSSSSYY**CCWWLLLLPPPPHHQQRRRRIIIMTTTTNNKKSSSKVVVVAAAADDEEGGGG",
+            24,
+            "Rhabdopleuridae Mitochondrial",
+            "---M---------------M---------------M---------------M------------",
+        ],
+        [
+            "FFLLSSSSYY**CCGWLLLLPPPPHHQQRRRRIIIMTTTTNNKKSSRRVVVVAAAADDEEGGGG",
+            25,
+            "Candidate Division SR1 and Gracilibacteria",
+            "---M-------------------------------M---------------M------------",
+        ],
+        [
+            "FFLLSSSSYY**CC*WLLLAPPPPHHQQRRRRIIIMTTTTNNKKSSRRVVVVAAAADDEEGGGG",
+            26,
+            "Pachysolen tannophilus Nuclear",
+            "-------------------M---------------M----------------------------",
+        ],
+        [
+            "FFLLSSSSYYQQCCWWLLLLPPPPHHQQRRRRIIIMTTTTNNKKSSRRVVVVAAAADDEEGGGG",
+            27,
+            "Karyorelict Nuclear",
+            "-----------------------------------M----------------------------",
+        ],
+        [
+            "FFLLSSSSYYQQCCWWLLLLPPPPHHQQRRRRIIIMTTTTNNKKSSRRVVVVAAAADDEEGGGG",
+            28,
+            "Condylostoma Nuclear",
+            "-----------------------------------M----------------------------",
+        ],
+        [
+            "FFLLSSSSYYYYCC*WLLLLPPPPHHQQRRRRIIIMTTTTNNKKSSRRVVVVAAAADDEEGGGG",
+            29,
+            "Mesodinium Nuclear",
+            "-----------------------------------M----------------------------",
+        ],
+        [
+            "FFLLSSSSYYEECC*WLLLLPPPPHHQQRRRRIIIMTTTTNNKKSSRRVVVVAAAADDEEGGGG",
+            30,
+            "Peritrich Nuclear",
+            "-----------------------------------M----------------------------",
+        ],
+        [
+            "FFLLSSSSYYEECCWWLLLLPPPPHHQQRRRRIIIMTTTTNNKKSSRRVVVVAAAADDEEGGGG",
+            31,
+            "Blastocrithidia Nuclear",
+            "-----------------------------------M----------------------------",
+        ],
+        [
+            "FFLLSSSSYY*WCC*WLLLLPPPPHHQQRRRRIIIMTTTTNNKKSSRRVVVVAAAADDEEGGGG",
+            32,
+            "Balanophoraceae Plastid",
+            "---M---------------M------------MMMM---------------M------------",
+        ],
+        [
+            "FFLLSSSSYYY*CCWWLLLLPPPPHHQQRRRRIIIMTTTTNNKKSSSKVVVVAAAADDEEGGGG",
+            33,
+            "Cephalodiscidae Mitochondrial",
+            "---M---------------M---------------M---------------M------------",
         ],
     ]
 ]
diff --git a/src/cogent3/evolve/ns_substitution_model.py b/src/cogent3/evolve/ns_substitution_model.py
index f367b0d6c..824b7442d 100644
--- a/src/cogent3/evolve/ns_substitution_model.py
+++ b/src/cogent3/evolve/ns_substitution_model.py
@@ -153,39 +153,47 @@ class DiscreteSubstitutionModel(_SubstitutionModel):
         )
 
 
+# todo inheritance hierarchy and constructor signatures
+# should match that of the stationary models
 class NonReversibleNucleotide(Parametric):
     """Base non-reversible nucleotide substitution model."""
 
     @extend_docstring_from(Parametric.__init__)
-    def __init__(self, *args, **kw):
-        Parametric.__init__(self, moltype.DNA.alphabet, *args, **kw)
+    def __init__(self, predicates=None, *args, **kw):
+        kw["alphabet"] = kw.get("alphabet", moltype.DNA.alphabet)
+        kw["predicates"] = predicates
+        Parametric.__init__(self, *args, **kw)
 
 
-class NonReversibleDinucleotide(Parametric):
+class NonReversibleDinucleotide(NonReversibleNucleotide):
     """Base non-reversible dinucleotide substitution model."""
 
     @extend_docstring_from(Parametric.__init__)
-    def __init__(self, *args, **kw):
-        Parametric.__init__(self, moltype.DNA.alphabet, motif_length=2, *args, **kw)
+    def __init__(self, predicates=None, *args, **kw):
+        kw["predicates"] = predicates
+        kw["motif_length"] = 2
+        NonReversibleNucleotide.__init__(self, *args, **kw)
 
 
-class NonReversibleTrinucleotide(Parametric):
+class NonReversibleTrinucleotide(NonReversibleNucleotide):
     """Base non-reversible trinucleotide substitution model."""
 
     @extend_docstring_from(Parametric.__init__)
-    def __init__(self, *args, **kw):
-        Parametric.__init__(self, moltype.DNA.alphabet, motif_length=3, *args, **kw)
+    def __init__(self, predicates=None, *args, **kw):
+        kw["predicates"] = predicates
+        kw["motif_length"] = 3
+        NonReversibleNucleotide.__init__(self, *args, **kw)
 
 
-class NonReversibleCodon(_Codon, Parametric):
+class NonReversibleCodon(_Codon, NonReversibleNucleotide):
     """Base non-reversible codon substitution model."""
 
     @extend_docstring_from(Parametric.__init__)
     def __init__(self, alphabet=None, gc=None, **kw):
         if gc is not None:
             alphabet = moltype.CodonAlphabet(gc=gc)
-        alphabet = alphabet or moltype.STANDARD_CODON
-        Parametric.__init__(self, alphabet, **kw)
+        kw["alphabet"] = alphabet or moltype.STANDARD_CODON
+        NonReversibleNucleotide.__init__(self, **kw)
 
 
 class StrandSymmetric(NonReversibleNucleotide):
diff --git a/src/cogent3/evolve/substitution_model.py b/src/cogent3/evolve/substitution_model.py
index 9a0c8db78..7bab7993f 100644
--- a/src/cogent3/evolve/substitution_model.py
+++ b/src/cogent3/evolve/substitution_model.py
@@ -256,6 +256,10 @@ class _SubstitutionModel(object):
 
     def to_rich_dict(self, for_pickle=False):
         data = deepcopy(self._serialisable)
+        # make sure kw matching higher level kw removed
+        for key in data.keys() - set(["kw"]):
+            data.get("kw", {}).pop(key, None)
+
         if not for_pickle:
             for key, value in data.items():
                 type_ = get_object_provenance(value)
@@ -266,7 +270,7 @@ class _SubstitutionModel(object):
                         pass
                     finally:
                         data[key] = value
-            if "predicates" in data and data["predicates"]:
+            if data.get("predicates", None):
                 data["predicates"] = [str(p) for p in data["predicates"]]
             data["type"] = get_object_provenance(self)
             data["version"] = __version__
@@ -927,25 +931,26 @@ class TimeReversibleNucleotide(_TimeReversibleNucleotide):
     """A nucleotide substitution model."""
 
     def __init__(self, *args, **kw):
-        _TimeReversibleNucleotide.__init__(self, moltype.DNA.alphabet, *args, **kw)
+        kw["alphabet"] = kw.get("alphabet", moltype.DNA.alphabet)
+        _TimeReversibleNucleotide.__init__(self, *args, **kw)
 
 
 class TimeReversibleDinucleotide(_TimeReversibleNucleotide):
     """A dinucleotide substitution model."""
 
     def __init__(self, *args, **kw):
-        _TimeReversibleNucleotide.__init__(
-            self, moltype.DNA.alphabet, motif_length=2, *args, **kw
-        )
+        kw["alphabet"] = kw.get("alphabet", moltype.DNA.alphabet)
+        kw["motif_length"] = 2
+        _TimeReversibleNucleotide.__init__(self, *args, **kw)
 
 
 class TimeReversibleTrinucleotide(_TimeReversibleNucleotide):
     """A trinucleotide substitution model."""
 
     def __init__(self, *args, **kw):
-        _TimeReversibleNucleotide.__init__(
-            self, moltype.DNA.alphabet, motif_length=3, *args, **kw
-        )
+        kw["alphabet"] = kw.get("alphabet", moltype.DNA.alphabet)
+        kw["motif_length"] = 3
+        _TimeReversibleNucleotide.__init__(self, *args, **kw)
 
 
 class TimeReversibleProtein(TimeReversible):
@@ -955,7 +960,9 @@ class TimeReversibleProtein(TimeReversible):
         alph = moltype.PROTEIN.alphabet
         if not with_selenocysteine:
             alph = alph.get_subset("U", excluded=True)
-        TimeReversible.__init__(self, alph, *args, **kw)
+
+        kw["alphabet"] = alph
+        TimeReversible.__init__(self, *args, **kw)
 
 
 def EmpiricalProteinMatrix(
@@ -1025,5 +1032,5 @@ class TimeReversibleCodon(_Codon, _TimeReversibleNucleotide):
     def __init__(self, alphabet=None, gc=None, **kw):
         if gc is not None:
             alphabet = moltype.CodonAlphabet(gc=gc)
-        alphabet = alphabet or moltype.STANDARD_CODON
-        _TimeReversibleNucleotide.__init__(self, alphabet, **kw)
+        kw["alphabet"] = alphabet or moltype.STANDARD_CODON
+        _TimeReversibleNucleotide.__init__(self, **kw)
diff --git a/src/cogent3/util/deserialise.py b/src/cogent3/util/deserialise.py
index 8bc290d83..459dd2058 100644
--- a/src/cogent3/util/deserialise.py
+++ b/src/cogent3/util/deserialise.py
@@ -292,7 +292,8 @@ def deserialise_substitution_model(data):
     if sm is None:
         alphabet = deserialise_alphabet(data.pop("alphabet"))
         klass = _get_class(data.pop("type"))
-        sm = klass(alphabet, **data)
+        data["alphabet"] = alphabet
+        sm = klass(**data)
 
     return sm
 


Test patch:
diff --git a/tests/test_core/test_alphabet.py b/tests/test_core/test_alphabet.py
index 396ca9b9b..26d90772b 100644
--- a/tests/test_core/test_alphabet.py
+++ b/tests/test_core/test_alphabet.py
@@ -7,6 +7,7 @@ import pickle
 
 from unittest import TestCase, main
 
+from numpy import unravel_index
 from numpy.testing import assert_equal
 
 from cogent3.core.alphabet import (
@@ -240,6 +241,14 @@ class CharAlphabetTests(TestCase):
         self.assertIsInstance(got, type(r))
         self.assertEqual(got.get_word_alphabet(2), wa)
 
+    def test_word_alphabet_order(self):
+        bases = "TCAG"
+        r = CharAlphabet(bases)
+        indices = [unravel_index(i, shape=(4, 4, 4)) for i in range(64)]
+        expect = tuple("".join([bases[b] for b in coord]) for coord in indices)
+        got = tuple(r.get_word_alphabet(3))
+        assert got == expect
+
     def test_from_string(self):
         """CharAlphabet from_string should return correct array"""
         r = CharAlphabet("UCAG")
diff --git a/tests/test_core/test_genetic_code.py b/tests/test_core/test_genetic_code.py
index 46d532830..65042f546 100644
--- a/tests/test_core/test_genetic_code.py
+++ b/tests/test_core/test_genetic_code.py
@@ -41,7 +41,7 @@ class GeneticCodeTests(TestCase):
         self.NcbiStandard = [
             "FFLLSSSSYY**CC*WLLLLPPPPHHQQRRRRIIIMTTTTNNKKSSRRVVVVAAAADDEEGGGG",
             1,
-            "Standard Nuclear",
+            "Standard",
             "---M---------------M---------------M----------------------------",
         ]
 
@@ -69,7 +69,7 @@ class GeneticCodeTests(TestCase):
         )
         self.assertEqual(sgc.start_codons, {"TTG": "M", "CTG": "M", "ATG": "M"})
         self.assertEqual(sgc.ID, 1)
-        self.assertEqual(sgc.name, "Standard Nuclear")
+        self.assertEqual(sgc.name, "Standard")
         self.assertEqual(sgc["UUU"], "F")
         self.assertEqual(sgc.is_start("ATG"), True)
         self.assertEqual(sgc.is_start("AAA"), False)
@@ -95,7 +95,7 @@ class GeneticCodeTests(TestCase):
             )
             self.assertEqual(sgc.start_codons, {"TTG": "M", "CTG": "M", "ATG": "M"})
             self.assertEqual(sgc.ID, 1)
-            self.assertEqual(sgc.name, "Standard Nuclear")
+            self.assertEqual(sgc.name, "Standard")
             self.assertEqual(sgc["TTT"], "F")
             self.assertEqual(sgc.is_start("ATG"), True)
             self.assertEqual(sgc.is_start("AAA"), False)
@@ -361,7 +361,7 @@ class GeneticCodeTests(TestCase):
 
     def test_get_code(self):
         """correctly return the genetic code"""
-        for code_id in [1, "1", "Standard Nuclear", DEFAULT]:
+        for code_id in [1, "1", "Standard", DEFAULT]:
             got = get_code(code_id)
             self.assertEqual(got, DEFAULT)
         got = get_code(2)
@@ -370,8 +370,8 @@ class GeneticCodeTests(TestCase):
     def test_available_codes(self):
         """avaialable_codes returns Table with correct shape"""
         codes = available_codes()
-        self.assertEqual(codes.shape, (17, 2))
-        self.assertEqual(codes[0, "Name"], "Standard Nuclear")
+        self.assertTrue(codes.shape[0] > 17)
+        self.assertEqual(codes[0, "Name"], "Standard")
 
     def test_to_table(self):
         """tests to_table method."""
@@ -411,7 +411,7 @@ class GeneticCodeTests(TestCase):
         )
         self.assertTrue("<table>" in got)
         self.assertTrue("</table>" in got)
-        self.assertIn("Standard Nuclear", got)
+        self.assertIn("Standard", got)
 
 
 # Run tests if called from command line
diff --git a/tests/test_core/test_moltype.py b/tests/test_core/test_moltype.py
index 470599204..274e35f87 100644
--- a/tests/test_core/test_moltype.py
+++ b/tests/test_core/test_moltype.py
@@ -1130,7 +1130,7 @@ class TestCodonAlphabet(_AlphabetTestCase):
     def test_constructing_from_func(self):
         """the CodonAlphabet function should support genetic code names as well"""
         alpha_int = CodonAlphabet(1)
-        alpha_name = CodonAlphabet("Standard Nuclear")
+        alpha_name = CodonAlphabet("Standard")
         self.assertEqual(alpha_int, alpha_name)
         alpha_int = CodonAlphabet(2)
         alpha_name = CodonAlphabet("Vertebrate Mitochondrial")
diff --git a/tests/test_util/test_deserialise.py b/tests/test_util/test_deserialise.py
index 886606280..67e882947 100644
--- a/tests/test_util/test_deserialise.py
+++ b/tests/test_util/test_deserialise.py
@@ -18,6 +18,11 @@ from cogent3 import (
 from cogent3.app.result import model_collection_result, model_result
 from cogent3.core import alignment, moltype
 from cogent3.evolve.models import get_model
+from cogent3.evolve.ns_substitution_model import (
+    NonReversibleDinucleotide,
+    NonReversibleTrinucleotide,
+    _sym_preds,
+)
 from cogent3.util.deserialise import (
     deserialise_likelihood_function,
     deserialise_object,
@@ -126,6 +131,18 @@ class TestDeserialising(TestCase):
         data = sm.to_json()
         got = deserialise_object(data)
         self.assertEqual(got.to_rich_dict(), sm.to_rich_dict())
+        sm = get_model("GNC")
+        data = sm.to_json()
+        got = deserialise_object(data)
+        self.assertEqual(got.to_rich_dict(), sm.to_rich_dict())
+        sm = NonReversibleDinucleotide(_sym_preds)
+        data = sm.to_json()
+        got = deserialise_object(data)
+        self.assertEqual(got.to_rich_dict(), sm.to_rich_dict())
+        sm = NonReversibleTrinucleotide(_sym_preds)
+        data = sm.to_json()
+        got = deserialise_object(data)
+        self.assertEqual(got.to_rich_dict(), sm.to_rich_dict())
 
     def test_roundtrip_discrete_time_submod(self):
         """discrete time substitution models to_json enables roundtrip"""