def test_decompose_gpindices(self): parent_gpindices = slice(10, 20) sibling_gpindices = slice(12, 14) x = mm._decompose_gpindices(parent_gpindices, sibling_gpindices) self.assertEqual(x, slice(2, 4)) parent_gpindices = slice(10, 20) sibling_gpindices = np.array([10, 12, 14], 'i') x = mm._decompose_gpindices(parent_gpindices, sibling_gpindices) self.assertEqual(list(x), list(np.array([0, 2, 4], 'i'))) parent_gpindices = np.array([2, 4, 6, 8, 10], 'i') sibling_gpindices = np.array([2, 6, 10], 'i') x = mm._decompose_gpindices(parent_gpindices, sibling_gpindices) self.assertEqual(list(x), list(np.array([0, 2, 4], 'i')))
def from_vector(self, v, close=False, dirty_value=True): """ Initialize this factory using a vector of parameters. Parameters ---------- v : numpy array The 1D vector of gate parameters. Length must == num_params() close : bool, optional Whether `v` is close to this factory's current set of parameters. Under some circumstances, when this is true this call can be completed more quickly. dirty_value : bool, optional The value to set this object's "dirty flag" to before exiting this call. This is passed as an argument so it can be updated *recursively*. Leave this set to `True` unless you know what you're doing. Returns ------- None """ assert ( self.gpindices is not None ), "Must set a ComposedOp's .gpindices before calling from_vector" for gate in self.factors: factor_local_inds = _gm._decompose_gpindices( self.gpindices, gate.gpindices) gate.from_vector(v[factor_local_inds], close, dirty_value) self.dirty = dirty_value
def hessian_wrt_params(self, wrt_filter1=None, wrt_filter2=None): """ Construct the Hessian of this error generator with respect to its parameters. This function returns a tensor whose first axis corresponds to the flattened operation matrix and whose 2nd and 3rd axes correspond to the parameters that are differentiated with respect to. Parameters ---------- wrt_filter1 : list or numpy.ndarray List of parameter indices to take 1st derivatives with respect to. (None means to use all the this operation's parameters.) wrt_filter2 : list or numpy.ndarray List of parameter indices to take 2nd derivatives with respect to. (None means to use all the this operation's parameters.) Returns ------- numpy array Hessian with shape (dimension^2, num_params1, num_params2) """ #TODO: in the furture could do this more cleverly so # each factor gets an appropriate wrt_filter instead of # doing all filtering at the end d2 = self.state_space.dim nP = self.num_params hessianMx = _np.zeros((d2**2, nP, nP), 'd') for eg in self.factors: factor_hessian = eg.hessian_wrt_params(None, None) # do filtering at end rel_gpindices = _modelmember._decompose_gpindices( self.gpindices, eg.gpindices) hessianMx[:, rel_gpindices, rel_gpindices] += factor_hessian[:, :, :] if wrt_filter1 is None: if wrt_filter2 is None: return hessianMx else: return _np.take(hessianMx, wrt_filter2, axis=2) else: if wrt_filter2 is None: return _np.take(hessianMx, wrt_filter1, axis=1) else: return _np.take(_np.take(hessianMx, wrt_filter1, axis=1), wrt_filter2, axis=2)
def to_vector(self): """ Get the parameters as an array of values. Returns ------- numpy array The parameters as a 1D array with length num_params(). """ assert ( self.gpindices is not None ), "Must set a ComposedOpFactory's .gpindices before calling to_vector" v = _np.empty(self.num_params, 'd') for gate in self.factors: factor_local_inds = _gm._decompose_gpindices( self.gpindices, gate.gpindices) v[factor_local_inds] = gate.to_vector() return v
def deriv_wrt_params(self, wrt_filter=None): """ The element-wise derivative this POVM effect vector. Construct a matrix whose columns are the derivatives of the POVM effect vector with respect to a single param. Thus, each column is of length dimension and there is one column per POVM effect vector parameter. Parameters ---------- wrt_filter : list or numpy.ndarray List of parameter indices to take derivative with respect to. (None means to use all the this operation's parameters.) Returns ------- numpy array Array of derivatives, shape == (dimension, num_params) """ if len(self.other_effects) == 0: return _np.zeros((self.dim, 0), 'd') # Complement vecs assumed real Np = len(self.gpindices_as_array()) neg_deriv = _np.zeros((self.dim, Np), 'd') for ovec in self.other_effects: local_inds = _modelmember._decompose_gpindices( self.gpindices, ovec.gpindices) #Note: other_vecs are not copies but other *sibling* effect vecs # so their gpindices index the same space as this complement vec's # does - so we need to "_decompose_gpindices" neg_deriv[:, local_inds] += ovec.deriv_wrt_params() derivMx = -neg_deriv if wrt_filter is None: return derivMx else: return _np.take(derivMx, wrt_filter, axis=1)
def _decompose_indices(x): return tuple( _modelmember._decompose_gpindices(self.gpindices, _np.array(x, _np.int64)))
def taylor_order_terms(self, order, max_polynomial_vars=100, return_coeff_polys=False): """ Get the `order`-th order Taylor-expansion terms of this error generator.. This function either constructs or returns a cached list of the terms at the given order. Each term is "rank-1", meaning that its action on a density matrix `rho` can be written: `rho -> A rho B` The coefficients of these terms are typically polynomials of the operation's parameters, where the polynomial's variable indices index the *global* parameters of the operation's parent (usually a :class:`Model`), not the operation's local parameter array (i.e. that returned from `to_vector`). Parameters ---------- order : int The order of terms to get. max_polynomial_vars : int, optional maximum number of variables the created polynomials can have. return_coeff_polys : bool Whether a parallel list of locally-indexed (using variable indices corresponding to *this* object's parameters rather than its parent's) polynomial coefficients should be returned as well. Returns ------- terms : list A list of :class:`RankOneTerm` objects. coefficients : list Only present when `return_coeff_polys == True`. A list of *compact* polynomial objects, meaning that each element is a `(vtape,ctape)` 2-tuple formed by concatenating together the output of :method:`Polynomial.compact`. """ assert(order == 0), \ "Error generators currently treat all terms as 0-th order; nothing else should be requested!" assert (return_coeff_polys is False) #Need to adjust indices b/c in error generators we (currently) expect terms to have local indices ret = [] for eg in self.factors: eg_terms = [ t.copy() for t in eg.taylor_order_terms( order, max_polynomial_vars, return_coeff_polys) ] mapvec = _np.ascontiguousarray( _modelmember._decompose_gpindices( self.gpindices, _modelmember._compose_gpindices( eg.gpindices, _np.arange(eg.num_params))), _np.int64) for t in eg_terms: # t.map_indices_inplace(lambda x: tuple(_modelmember._decompose_gpindices( # # map global to *local* indices # self.gpindices, _modelmember._compose_gpindices(eg.gpindices, _np.array(x, _np.int64))))) t.mapvec_indices_inplace(mapvec) ret.extend(eg_terms) return ret