@@ -1761,6 +1761,7 @@ def create_simple_equivalent(self, builtin_basis_name=None):
17611761 builtin_basis_name = first_comp_name # if all components have the same name
17621762 return BuiltinBasis (builtin_basis_name , self .elsize , sparse = self .sparse )
17631763
1764+
17641765class OuterProdBasis (TensorProdBasis ):
17651766
17661767 def __init__ (self , component_bases , name = None , longname = None , squeeze = True ):
@@ -1832,282 +1833,3 @@ def create_equivalent(self, builtin_basis_name):
18321833 def create_simple_equivalent (self , builtin_basis_name = None ):
18331834 raise NotImplementedError ()
18341835
1835-
1836- class EmbeddedBasis (LazyBasis ):
1837- """
1838- A basis that embeds a basis for a smaller state space within a larger state space.
1839-
1840- The elements of an EmbeddedBasis are therefore just embedded versions
1841- of the elements of the basis that is embedded.
1842-
1843- Parameters
1844- ----------
1845- basis_to_embed : Basis
1846- The basis being embedded.
1847-
1848- state_space_labels : StateSpaceLabels
1849- An object describing the struture of the entire state space.
1850-
1851- target_labels : list or tuple
1852- The labels contained in `stateSpaceLabels` which demarcate the
1853- portions of the state space acted on by `basis_to_embed`.
1854-
1855- name : str, optional
1856- The name of this basis. If `None`, the names of `basis_to_embed`
1857- is joined with ':' characters to the elements of `target_labels`.
1858-
1859- longname : str, optional
1860- A longer description of this basis. If `None`, then a long name is
1861- automatically generated.
1862- """
1863-
1864- @classmethod
1865- def embed_label (cls , lbl , target_labels ):
1866- """
1867- Gets the EmbeddedBasis label for `lbl`.
1868-
1869- Convenience method that gives the EmbeddedBasis label for `lbl`
1870- without needing to construct the `EmbeddedBasis`. E.g. `"XX:1,2"`.
1871-
1872- Parameters
1873- ----------
1874- lbl : str
1875- Un-embedded basis element label, e.g. `"XX"`.
1876-
1877- target_labels : tuple
1878- The target state space labels upon which this basis element
1879- will be embedded, e.g. `(1,2)`
1880-
1881- Returns
1882- -------
1883- str
1884- The embedded-basis-element label as an EmbeddedBasis would
1885- assign it. E.g. `"XX:1,2"`.
1886- """
1887- return "%s:%s" % (lbl , "," .join (map (str , target_labels )))
1888-
1889- @classmethod
1890- def unembed_label (cls , lbl , target_labels ):
1891- """
1892- Convenience method that performs the reverse of :meth:`embed_label`
1893-
1894- Parameters
1895- ----------
1896- lbl : str
1897- Embedded basis element label, e.g. `"XX:1,2"`.
1898-
1899- target_labels : tuple
1900- The target state space labels upon which this basis element
1901- will be embedded, e.g. `(1,2)`
1902-
1903- Returns
1904- -------
1905- str
1906- The un-embedded label, e.g. `"XX"`.
1907- """
1908- suffix = ":" + "," .join (map (str , target_labels ))
1909- if lbl .endswith (suffix ):
1910- return lbl [:- len (suffix )]
1911- else :
1912- raise ValueError ("Cannot unembed '%s' - doesn't end in '%s'!" % (lbl , suffix ))
1913-
1914- def __init__ (self , basis_to_embed , state_space , target_labels , name = None , longname = None ):
1915- '''
1916- Create a new EmbeddedBasis.
1917-
1918- Parameters
1919- ----------
1920- basis_to_embed : Basis
1921- The basis being embedded.
1922-
1923- state_space : StateSpace
1924- An object describing the struture of the entire state space.
1925-
1926- target_labels : list or tuple
1927- The labels contained in `stateSpaceLabels` which demarcate the
1928- portions of the state space acted on by `basis_to_embed`.
1929-
1930- name : str, optional
1931- The name of this basis. If `None`, the names of `basis_to_embed`
1932- is joined with ':' characters to the elements of `target_labels`.
1933-
1934- longname : str, optional
1935- A longer description of this basis. If `None`, then a long name is
1936- automatically generated.
1937- '''
1938- from pygsti .baseobjs .statespace import StateSpace as _StateSpace
1939- self .embedded_basis = basis_to_embed
1940- self .target_labels = target_labels
1941- self .state_space = _StateSpace .cast (state_space )
1942-
1943- if name is None :
1944- name = ':' .join ((basis_to_embed .name ,) + tuple (map (str , target_labels )))
1945- if longname is None :
1946- longname = "Embedded %s basis as %s within %s" % \
1947- (basis_to_embed .name , ':' .join (map (str , target_labels )), str (self .state_space ))
1948-
1949- real = basis_to_embed .real
1950- sparse = basis_to_embed .sparse
1951-
1952- super (EmbeddedBasis , self ).__init__ (name , longname , real , sparse )
1953-
1954- def _to_nice_serialization (self ):
1955- state = super ()._to_nice_serialization ()
1956- state .update ({'name' : self .name ,
1957- 'longname' : self .longname ,
1958- 'state_space' : self .state_space .to_nice_serialization (),
1959- 'embedded_basis' : self .embedded_basis .to_nice_serialization ()
1960- })
1961- return state
1962-
1963- @classmethod
1964- def _from_nice_serialization (cls , state ):
1965- basis_to_embed = Basis .from_nice_serialization (state ['embedded_basis' ])
1966- state_space = _StateSpace .from_nice_serialization (state ['state_space' ])
1967- return cls (basis_to_embed , state_space , state ['target_labels' ], state ['name' ], state ['longname' ])
1968-
1969- @property
1970- def dim (self ):
1971- """
1972- The dimension of the vector space this basis fully or partially
1973- spans. Equivalently, the length of the `vector_elements` of the
1974- basis.
1975- """
1976- return self .state_space .dim
1977-
1978- @property
1979- def size (self ):
1980- """
1981- The number of elements (or vector-elements) in the basis.
1982- """
1983- return self .embedded_basis .size
1984-
1985- @property
1986- def elshape (self ):
1987- """
1988- The shape of each element. Typically either a length-1 or length-2
1989- tuple, corresponding to vector or matrix elements, respectively.
1990- Note that *vector elements* always have shape `(dim,)` (or `(dim,1)`
1991- in the sparse case).
1992- """
1993- elndim = self .embedded_basis .elndim
1994- if elndim == 2 : # a "matrix" basis
1995- d = int (_np .sqrt (self .dim ))
1996- assert (d ** 2 == self .dim ), \
1997- "Dimension of state_space must be a perfect square when embedding a matrix basis"
1998- elshape = (d , d )
1999- elif elndim == 1 :
2000- elshape = (self .dim ,)
2001- else :
2002- raise ValueError ("Can only embed bases with .elndim == 1 or 2 (received %d)!" % elndim )
2003- return elshape
2004-
2005- def __hash__ (self ):
2006- return hash (tuple (hash (self .embedded_basis ), self .target_labels , self .state_space ))
2007-
2008- def _lazy_build_elements (self ):
2009- """ Take a dense or sparse basis matrix and embed it. """
2010- #LAZY building of elements (in case we never need them)
2011- if self .elndim == 2 : # then use EmbeddedOp to do matrix
2012- from ..modelmembers .operations import StaticArbitraryOp
2013- from ..modelmembers .operations import EmbeddedOp
2014- sslbls = self .state_space .copy ()
2015- sslbls .reduce_dims_densitymx_to_state_inplace () # because we're working with basis matrices not gates
2016-
2017- if self .sparse :
2018- self ._elements = []
2019- for spmx in self .embedded_basis .elements :
2020- mxAsOp = StaticArbitraryOp (spmx .to_dense (), evotype = 'statevec' )
2021- self ._elements .append (EmbeddedOp (sslbls , self .target_labels ,
2022- mxAsOp ).to_sparse ())
2023- else :
2024- self ._elements = _np .zeros ((self .size ,) + self .elshape , 'complex' )
2025- for i , mx in enumerate (self .embedded_basis .elements ):
2026- self ._elements [i ] = EmbeddedOp (
2027- sslbls , self .target_labels , StaticArbitraryOp (mx , evotype = 'statevec' )
2028- ).to_dense (on_space = 'HilbertSchmidt' )
2029- else :
2030- # we need to perform embedding using vectors rather than matrices - doable, but
2031- # not needed yet, so defer implementation to later.
2032- raise NotImplementedError ("Embedding *vector*-type bases not implemented yet" )
2033-
2034- def _lazy_build_labels (self ):
2035- self ._labels = [EmbeddedBasis .embed_label (lbl , self .target_labels )
2036- for lbl in self .embedded_basis .labels ]
2037-
2038- def _copy_with_toggled_sparsity (self ):
2039- return EmbeddedBasis (self .embedded_basis ._copy_with_toggled_sparsity (),
2040- self .state_space ,
2041- self .target_labels ,
2042- self .name , self .longname )
2043-
2044- def is_equivalent (self , other , sparseness_must_match = True ):
2045- """
2046- Tests whether this basis is equal to another basis, optionally ignoring sparseness.
2047-
2048- Parameters
2049- -----------
2050- other : Basis or str
2051- The basis to compare with.
2052-
2053- sparseness_must_match : bool, optional
2054- If `False` then comparison ignores differing sparseness, and this function
2055- returns `True` when the two bases are equal except for their `.sparse` values.
2056-
2057- Returns
2058- -------
2059- bool
2060- """
2061- otherIsBasis = isinstance (other , EmbeddedBasis )
2062- if not otherIsBasis : return False # can't be equal to a non-EmbeddedBasis
2063- if self .target_labels != other .target_labels or self .state_space != other .state_space :
2064- return False
2065- return self .embedded_basis .is_equivalent (other .embedded_basis , sparseness_must_match )
2066-
2067- def create_equivalent (self , builtin_basis_name ):
2068- """
2069- Create an equivalent basis with components of type `builtin_basis_name`.
2070-
2071- Create a Basis that is equivalent in structure & dimension to this
2072- basis but whose simple components (perhaps just this basis itself) is
2073- of the builtin basis type given by `builtin_basis_name`.
2074-
2075- Parameters
2076- ----------
2077- builtin_basis_name : str
2078- The name of a builtin basis, e.g. `"pp"`, `"gm"`, or `"std"`. Used to
2079- construct the simple components of the returned basis.
2080-
2081- Returns
2082- -------
2083- EmbeddedBasis
2084- """
2085- equiv_embedded = self .embedded_basis .create_equivalent (builtin_basis_name )
2086- return EmbeddedBasis (equiv_embedded , self .state_space , self .target_labels )
2087-
2088- def create_simple_equivalent (self , builtin_basis_name = None ):
2089- """
2090- Create a basis of type `builtin_basis_name` whose elements are compatible with this basis.
2091-
2092- Create a simple basis *and* one without components (e.g. a
2093- :class:`TensorProdBasis`, is a simple basis w/components) of the
2094- builtin type specified whose dimension is compatible with the
2095- *elements* of this basis. This function might also be named
2096- "element_equivalent", as it returns the `builtin_basis_name`-analogue
2097- of the standard basis that this basis's elements are expressed in.
2098-
2099- Parameters
2100- ----------
2101- builtin_basis_name : str, optional
2102- The name of the built-in basis to use. If `None`, then a
2103- copy of this basis is returned (if it's simple) or this
2104- basis's name is used to try to construct a simple and
2105- component-free version of the same builtin-basis type.
2106-
2107- Returns
2108- -------
2109- Basis
2110- """
2111- if builtin_basis_name is None :
2112- builtin_basis_name = self .embedded_basis .name # default
2113- return BuiltinBasis (builtin_basis_name , self .elsize , sparse = self .sparse )
0 commit comments