def simplify(self, full=True): new_scalar = 1 for factor in self: if is_zero(factor): return 0 if not is_one(factor): new_scalar *= simplify(factor) new_scalar = expand(new_scalar) if isinstance(new_scalar, SumOfScalars): return simplify(new_scalar) if _is_sequenced_scalar(new_scalar): if len(new_scalar) == 0: return 0 if len(new_scalar) == 1: return simplify(new_scalar[0]) if isinstance(new_scalar, ProductOfScalars): if any(isinstance(s, ProductOfScalars) for s in new_scalar): raise RuntimeError("factors of product should not be products") if full: changed = True while changed: changed = new_scalar._combine_factors() return new_scalar
def simplify(self, full=True): new_scalar = 0 expanded = self.expand() if not isinstance(expanded, SumOfScalars): return simplify(expanded) if is_number(expanded): return expanded for term in expanded: if not is_zero(term): new_scalar += simplify(term) if _is_sequenced_scalar(new_scalar): if len(new_scalar) == 0: new_scalar = 0 if len(new_scalar) == 1: new_scalar = simplify(new_scalar[0]) if isinstance(new_scalar, SumOfScalars): if any(isinstance(s, SumOfScalars) for s in new_scalar): raise RuntimeError("terms of sum should not be sum after expand") if full: changed = True while changed: changed = new_scalar._combine_terms() return new_scalar
def _mul_state(self, state): # compute output state for each term of the operator new_state = State([]) for base_op, scalar in self._terms.items(): if is_zero(scalar): continue new_state += scalar * (base_op * state) return new_state
def is_zero(self): for f in self._factors: if isinstance(f, float): if math.isclose(f, 0, abs_tol=1e-16): return True else: if is_zero(f): return True return False
def __add__(self, other): # Check if one is zero if is_zero(other): return copy(self) if is_zero(self): return copy(other) if not is_scalar(other): return NotImplemented if isinstance(other, SumOfScalars): return other + self # Check if multiples self_multiple, self_scalar = _get_multiple_of_scalar(self) other_multiple, other_scalar = _get_multiple_of_scalar(other) if self_scalar == other_scalar: return copy(self_scalar) * (self_multiple + other_multiple) return SumOfScalars([self, other])
def test_iszero_prod(): a = SingleVarFunctionScalar('a', 'x') b = SingleVarFunctionScalar('b', 'x') prod = a * b prod[1] = 1e-17 prod = simplify(prod) assert prod == 0 assert is_zero(prod)
def __mul__(self, other): if is_zero(other): return 0 if is_one(other): return copy(self) if not is_scalar(other): return NotImplemented if isinstance(other, ProductOfScalars): return other * self return ProductOfScalars([self, other])
def __add__(self, other): # Check if one is zero if is_zero(other): return copy(self) if is_zero(self): return copy(other) if not is_scalar(other): return NotImplemented if is_number(other): return SumOfScalars([self._terms[0] + other] + self._terms[1:]) if isinstance(other, SumOfScalars): new_scalar = copy(self) for term in other: new_scalar += term return new_scalar # Check if the addition between other and any term can be simplified for i, term in enumerate(self._terms): if i == 0: continue sm = term + other if not isinstance(sm, SumOfScalars): return SumOfScalars([sm] + self._terms[:i] + self._terms[i + 1:]) return SumOfScalars(self._terms + [other])
def inner_product(self, other, first_replace_var=True): """ Takes the inner product with another :class:`~.State`. Parameters ---------- other : :class:`.State` The right hand side of the inner product. first_replace_var (optional) : bool Whether to replace all varibles of the right hand side with new ones (only done for `SingleVarFunctionScalar`). This can be useful when the two states are actually integrals over the variables and should therefore be different. Returns ------- :class`~.scalar.Scalar` The inner product """ if not isinstance(other, self.__class__): raise NotImplementedError( f"inner product is not implemented for {type(other)}") if not self._compatible(other): raise ValueError( f"other ({other}) is not compatible with self ({self})") # NOTE if BaseState are assumed to be orthogonal be don't need to do the # product of base states. if first_replace_var: other = replace_var(other) inner = 0 for self_base_state, self_scalar in self._terms.items(): for other_base_state, other_scalar in other._terms.items(): factors = [ self_scalar.conjugate(), other_scalar, self_base_state.inner_product(other_base_state), ] if any(is_zero(factor) for factor in factors): continue inner += (self_scalar.conjugate() * other_scalar ) * self_base_state.inner_product(other_base_state) return inner
def _mul_operator(self, operator): new_op = Operator() for self_base_op, self_scalar in self._terms.items(): for other_base_op, other_scalar in operator._terms.items(): new_base_op = BaseOperator(self_base_op._left, other_base_op._right) new_scalar = self_base_op._right.inner_product( other_base_op._left) * self_scalar * other_scalar # Integrate out variables which are not in base operator # TODO, should this be optional? scalar_variables = get_variables(new_scalar) - get_variables( new_base_op) new_scalar = integrate(new_scalar, scalar_variables) if is_zero(new_scalar): continue new_op._terms[new_base_op] += new_scalar return new_op
def is_zero(self): return all(is_zero(s) for s in self)