def eval(cls, a, b): """The Commutator [A,B] is on canonical form if A < B. """ if not (a and b): return S.Zero if a == b: return Integer(2) * a**2 if a.is_commutative or b.is_commutative: return Integer(2) * a * b # [xA,yB] -> xy*[A,B] # from sympy.physics.qmul import QMul c_part = [] nc_part = [] nc_part2 = [] if isinstance(a, Mul): c_part, nc_part = split_commutative_parts(a) if isinstance(b, Mul): c_part2, nc_part2 = split_commutative_parts(b) c_part.extend(c_part2) if c_part: a = nc_part or [a] b = nc_part2 or [b] return Mul(Mul(*c_part), cls(Mul(*a), Mul(*b))) # Canonical ordering of arguments if a.compare(b) == 1: return cls(b, a)
def eval(cls, a, b): """The Commutator [A,B] is on canonical form if A < B. """ if not (a and b): return S.Zero if a == b: return Integer(2)*a**2 if a.is_commutative or b.is_commutative: return Integer(2)*a*b # [xA,yB] -> xy*[A,B] # from sympy.physics.qmul import QMul c_part = [] nc_part = [] nc_part2 = [] if isinstance(a, Mul): c_part, nc_part = split_commutative_parts(a) if isinstance(b, Mul): c_part2, nc_part2 = split_commutative_parts(b) c_part.extend(c_part2) if c_part: a = nc_part or [a] b = nc_part2 or [b] return Mul(Mul(*c_part), cls(Mul(*a), Mul(*b))) # Canonical ordering of arguments if a.compare(b) == 1: return cls(b,a)
def __separate_scalar_factor(monomial): """Separate the constant factor from a monomial. """ scalar_factor = 1 if is_number_type(monomial): return S.One, monomial if monomial == 0: return S.One, 0 comm_factors, _ = split_commutative_parts(monomial) if len(comm_factors) > 0: if isinstance(comm_factors[0], Number): scalar_factor = comm_factors[0] if scalar_factor != 1: return monomial / scalar_factor, scalar_factor else: return monomial, scalar_factor
def flatten(cls, args): # TODO: disallow nested TensorProducts. c_part = [] nc_parts = [] for arg in args: if isinstance(arg, Mul): cp, ncp = split_commutative_parts(arg) ncp = Mul(*ncp) else: if arg.is_commutative: cp = [arg]; ncp = 1 else: cp = []; ncp = arg c_part.extend(cp) nc_parts.append(ncp) return c_part, nc_parts
def separate_scalar_factor(monomial): """Separate the constant factor from a monomial. """ scalar_factor = 1 if isinstance(monomial, int): return S.One, monomial if monomial == 0: return S.One, 0 comm_factors, non_commfactors = split_commutative_parts(monomial) if len(comm_factors) > 0: if isinstance(comm_factors[0], Number): scalar_factor = comm_factors[0] if scalar_factor != 1: return monomial / scalar_factor, scalar_factor else: return monomial, scalar_factor
def get_support_variables(polynomial): """Gets the support of a polynomial. """ support = [] if is_number_type(polynomial): return support polynomial = polynomial.expand() for monomial in polynomial.as_coefficients_dict(): monomial, _ = __separate_scalar_factor(monomial) symbolic_support = flatten(split_commutative_parts(monomial)) for s in symbolic_support: if isinstance(s, Pow): base = s.base if is_adjoint(base): base = base.adjoint() support.append(base) elif is_adjoint(s): support.append(s.adjoint()) elif isinstance(s, Operator): support.append(s) return support
def get_support(variables, polynomial): """Gets the support of a polynomial. """ support = [] if is_number_type(polynomial): support.append([0] * len(variables)) return support polynomial = polynomial.expand() for monomial in polynomial.as_coefficients_dict(): tmp_support = [0] * len(variables) monomial, _ = __separate_scalar_factor(monomial) symbolic_support = flatten(split_commutative_parts(monomial)) for s in symbolic_support: if isinstance(s, Pow): base = s.base if is_adjoint(base): base = base.adjoint() tmp_support[variables.index(base)] = s.exp elif is_adjoint(s): tmp_support[variables.index(s.adjoint())] = 1 elif isinstance(s, (Operator, Symbol)): tmp_support[variables.index(s)] = 1 support.append(tmp_support) return support
def get_support(variables, polynomial): """Gets the support of a polynomial. """ support = [] if isinstance(polynomial, (int, float, complex)): support.append([0] * len(variables)) return support polynomial = polynomial.expand() for monomial in polynomial.as_coefficients_dict(): tmp_support = [0] * len(variables) monomial, _ = separate_scalar_factor(monomial) symbolic_support = flatten(split_commutative_parts(monomial)) for s in symbolic_support: if isinstance(s, Pow): base = s.base if isinstance(base, Dagger): base = Dagger(base) tmp_support[variables.index(base)] = s.exp elif isinstance(s, Dagger): tmp_support[variables.index(Dagger(s))] = 1 elif isinstance(s, Operator): tmp_support[variables.index(s)] = 1 support.append(tmp_support) return support
def get_support(variables, polynomial): """Gets the support of a polynomial. """ support = [] if isinstance(polynomial, (int, float, complex)): support.append([0] * len(variables)) return support polynomial = polynomial.expand() for monomial in polynomial.as_coefficients_dict(): tmp_support = [0] * len(variables) monomial, scalar = separate_scalar_factor(monomial) symbolic_support = flatten(split_commutative_parts(monomial)) for s in symbolic_support: if isinstance(s, Pow): base = s.base if isinstance(base, Dagger): base = Dagger(base) tmp_support[variables.index(base)] = s.exp elif isinstance(s, Dagger): tmp_support[variables.index(Dagger(s))] = 1 elif isinstance(s, Operator): tmp_support[variables.index(s)] = 1 support.append(tmp_support) return support
def fast_substitute(monomial, old_sub, new_sub): """Experimental fast substitution routine that considers only restricted cases of noncommutative algebras. In rare cases, it fails to find a substitution. Use it with proper testing. :param monomial: The monomial with parts need to be substituted. :param old_sub: The part to be replaced. :param new_sub: The replacement. """ if is_number_type(monomial): return monomial if monomial.is_Add: return sum([fast_substitute(element, old_sub, new_sub) for element in monomial.as_ordered_terms()]) comm_factors, ncomm_factors = split_commutative_parts(monomial) old_comm_factors, old_ncomm_factors = split_commutative_parts(old_sub) # This is a temporary hack if not isinstance(new_sub, int) and not isinstance(new_sub, float): new_comm_factors, _ = split_commutative_parts(new_sub) else: new_comm_factors = [new_sub] comm_monomial = 1 is_constant_term = False if len(comm_factors) == 1 and is_number_type(comm_factors[0]): is_constant_term = True comm_monomial = comm_factors[0] if not is_constant_term and len(comm_factors) > 0: for comm_factor in comm_factors: comm_monomial *= comm_factor if len(old_comm_factors) > 0: comm_old_sub = 1 for comm_factor in old_comm_factors: comm_old_sub *= comm_factor comm_new_sub = 1 for comm_factor in new_comm_factors: comm_new_sub *= comm_factor # Dummy heuristic to get around retarded SymPy bug if isinstance(comm_old_sub, Pow): # In this case, we are in trouble old_base = comm_old_sub.base if comm_monomial.has(old_base): old_degree = comm_old_sub.exp new_monomial = 1 match = False for factor in comm_monomial.as_ordered_factors(): if factor.has(old_base): if isinstance(factor, Pow): degree = factor.exp if degree >= old_degree: match = True new_monomial *= \ old_base**(degree-old_degree) * \ comm_new_sub else: new_monomial *= factor else: new_monomial *= factor if match: comm_monomial = new_monomial else: comm_monomial = comm_monomial.subs(comm_old_sub, comm_new_sub) if len(ncomm_factors) == 0 or len(old_ncomm_factors) == 0: return comm_monomial # old_factors = old_sub.as_ordered_factors() # factors = monomial.as_ordered_factors() new_var_list = [] new_monomial = 1 match = False left_remainder = 1 right_remainder = 1 for i in range(len(ncomm_factors) - len(old_ncomm_factors) + 1): for j in range(len(old_ncomm_factors)): if isinstance(ncomm_factors[i + j], Number) and \ ((not isinstance(old_ncomm_factors[j], Number) or ncomm_factors[i + j] != old_ncomm_factors[j])): break if isinstance(ncomm_factors[i + j], Symbol) and \ (not isinstance(old_ncomm_factors[j], Operator) or (isinstance(old_ncomm_factors[j], Symbol) and ncomm_factors[i + j] != old_ncomm_factors[j])): break if isinstance(ncomm_factors[i + j], Operator) and \ isinstance(old_ncomm_factors[j], Operator) and \ ncomm_factors[i + j] != old_ncomm_factors[j]: break if is_adjoint(ncomm_factors[i + j]) and \ (not is_adjoint(old_ncomm_factors[j]) or ncomm_factors[i + j] != old_ncomm_factors[j]): break if not is_adjoint(ncomm_factors[i + j]) and \ not isinstance(ncomm_factors[i + j], Pow) and \ is_adjoint(old_ncomm_factors[j]): break if isinstance(ncomm_factors[i + j], Pow): if isinstance(old_ncomm_factors[j], Pow): old_base = old_ncomm_factors[j].base old_degree = old_ncomm_factors[j].exp else: old_base = old_ncomm_factors[j] old_degree = 1 if old_base != ncomm_factors[i + j].base: break if old_degree > ncomm_factors[i + j].exp: break if old_degree < ncomm_factors[i + j].exp: if j != len(old_ncomm_factors) - 1: if j != 0: break else: left_remainder = old_base ** ( ncomm_factors[i + j].exp - old_degree) else: right_remainder = old_base ** ( ncomm_factors[i + j].exp - old_degree) if isinstance(ncomm_factors[i + j], Operator) and \ isinstance(old_ncomm_factors[j], Pow): break else: match = True if not match: new_var_list.append(ncomm_factors[i]) else: new_monomial = 1 for var in new_var_list: new_monomial *= var new_monomial *= left_remainder * new_sub * right_remainder for j in range(i + len(old_ncomm_factors), len(ncomm_factors)): new_monomial *= ncomm_factors[j] new_monomial *= comm_monomial break else: if not is_constant_term and len(comm_factors) > 0: new_monomial = comm_monomial for factor in ncomm_factors: new_monomial *= factor else: return monomial if not isinstance(new_sub, (float, int, complex)) and new_sub.is_Add: return expand(new_monomial) else: return new_monomial
def tensor_product_simp_Mul(e): """Simplify a Mul with TensorProducts. Current the main use of this is to simplify a ``Mul`` of ``TensorProduct``s to a ``TensorProduct`` of ``Muls``. It currently only works for relatively simple cases where the initial ``Mul`` only has scalars and raw ``TensorProduct``s, not ``Add``, ``Pow``, ``Commutator``s of ``TensorProduct``s. Parameters ========== e : Expr A ``Mul`` of ``TensorProduct``s to be simplified. Returns ======= e : Expr A ``TensorProduct`` of ``Mul``s. Examples ======== This is an example of the type of simplification that this function performs:: >>> from sympy.physics.quantum.tensorproduct import tensor_product_simp_Mul, TensorProduct >>> from sympy import Symbol >>> A = Symbol('A',commutative=False) >>> B = Symbol('B',commutative=False) >>> C = Symbol('C',commutative=False) >>> D = Symbol('D',commutative=False) >>> e = TensorProduct(A,B)*TensorProduct(C,D) >>> e AxB*CxD >>> tensor_product_simp_Mul(e) (A*C)x(B*D) """ # TODO: This won't work with Muls that have other composites of # TensorProducts, like an Add, Pow, Commutator, etc. # TODO: This only works for the equivalent of single Qbit gates. if not isinstance(e, Mul): return e c_part, nc_part = split_commutative_parts(e) n_nc = len(nc_part) if n_nc == 0 or n_nc == 1: return e elif e.has(TensorProduct): current = nc_part[0] if not isinstance(current, TensorProduct): raise TypeError('TensorProduct expected, got: %r' % current) n_terms = len(current.args) new_args = list(current.args) for next in nc_part[1:]: # TODO: check the hilbert spaces of next and current here. if isinstance(next, TensorProduct): if n_terms != len(next.args): raise QuantumError( 'TensorProducts of different lengths: %r and %r' % \ (current, next) ) for i in range(len(new_args)): new_args[i] = new_args[i]*next.args[i] else: # this won't quite work as we don't want next in the TensorProduct for i in range(len(new_args)): new_args[i] = new_args[i]*next current = next return Mul(*c_part)*TensorProduct(*new_args) else: return e
def fast_substitute(monomial, old_sub, new_sub): """Experimental fast substitution routine that considers only restricted cases of noncommutative algebras. In rare cases, it fails to find a substitution. Use it with proper testing. :param monomial: The monomial with parts need to be substituted. :param old_sub: The part to be replaced. :param new_sub: The replacement. """ if isinstance(monomial, Number): return monomial if isinstance(monomial, int): return monomial comm_factors, ncomm_factors = split_commutative_parts(monomial) old_comm_factors, old_ncomm_factors = split_commutative_parts(old_sub) # This is a temporary hack if not isinstance(new_sub, int): new_comm_factors, new_ncomm_factors = split_commutative_parts(new_sub) comm_monomial = 1 is_constant_term = False if len(comm_factors) == 1 and isinstance(comm_factors[0], Number): is_constant_term = True comm_monomial = comm_factors[0] if not is_constant_term and len(comm_factors) > 0: for comm_factor in comm_factors: comm_monomial *= comm_factor if len(old_comm_factors) > 0: comm_old_sub = 1 for comm_factor in old_comm_factors: comm_old_sub *= comm_factor comm_new_sub = 1 for comm_factor in new_comm_factors: comm_new_sub *= comm_factor comm_monomial = comm_monomial.subs(comm_old_sub, comm_new_sub) if len(ncomm_factors) == 0 or len(old_ncomm_factors) == 0: return comm_monomial # old_factors = old_sub.as_ordered_factors() # factors = monomial.as_ordered_factors() new_var_list = [] new_monomial = 1 match = False left_remainder = 1 right_remainder = 1 for i in range(len(ncomm_factors) - len(old_ncomm_factors) + 1): for j in range(len(old_ncomm_factors)): if isinstance(ncomm_factors[i + j], Number) and \ ((not isinstance(old_ncomm_factors[j], Number) or ncomm_factors[i + j] != old_ncomm_factors[j])): break if isinstance(ncomm_factors[i + j], Symbol) and \ (not isinstance(old_ncomm_factors[j], Operator) or (isinstance(old_ncomm_factors[j], Symbol) and ncomm_factors[i + j] != old_ncomm_factors[j])): break if isinstance(ncomm_factors[i + j], Operator) and \ isinstance(old_ncomm_factors[j], Operator) and \ ncomm_factors[i + j] != old_ncomm_factors[j]: break if isinstance(ncomm_factors[i + j], Dagger) and \ (not isinstance(old_ncomm_factors[j], Dagger) or ncomm_factors[i + j] != old_ncomm_factors[j]): break if not isinstance(ncomm_factors[i + j], Dagger) and \ not isinstance(ncomm_factors[i + j], Pow) and \ isinstance(old_ncomm_factors[j], Dagger): break if isinstance(ncomm_factors[i + j], Pow): old_degree = 1 old_base = 1 if isinstance(old_ncomm_factors[j], Pow): old_base = old_ncomm_factors[j].base old_degree = old_ncomm_factors[j].exp else: old_base = old_ncomm_factors[j] if old_base != ncomm_factors[i + j].base: break if old_degree > ncomm_factors[i + j].exp: break if old_degree < ncomm_factors[i + j].exp: if j != len(old_ncomm_factors) - 1: if j != 0: break else: left_remainder = old_base**( ncomm_factors[i + j].exp - old_degree) else: right_remainder = old_base**(ncomm_factors[i + j].exp - old_degree) if isinstance(ncomm_factors[i + j], Operator) and \ isinstance(old_ncomm_factors[j], Pow): break else: match = True if not match: new_var_list.append(ncomm_factors[i]) else: new_monomial = 1 for var in new_var_list: new_monomial *= var new_monomial *= left_remainder * new_sub * right_remainder for j in range(i + len(old_ncomm_factors), len(ncomm_factors)): new_monomial *= ncomm_factors[j] new_monomial *= comm_monomial break else: if not is_constant_term and len(comm_factors) > 0: new_monomial = comm_monomial for factor in ncomm_factors: new_monomial *= factor else: return monomial return new_monomial