def extract_all_operators(e_orig): """ Extract all unique operators in the normal ordered for of a given operator expression, including composite operators. The resulting list of operators are sorted in increasing order. """ if debug: print("extract_all_operators: ", e_orig) if isinstance(e_orig, Operator): return [e_orig] e = drop_c_number_terms(normal_ordered_form(e_orig.expand(), independent=True)) if isinstance(e, Pow) and isinstance(e.base, Operator): return [e] ops = [] if isinstance(e, Add): for arg in e.args: ops += extract_all_operators(arg) if isinstance(e, Mul): op_f = [f for f in e.args if (isinstance(f, Operator) or (isinstance(f, Pow) and isinstance(f.base, Operator)))] ops.append(Mul(*op_f)) ops += op_f unique_ops = list(set(ops)) sorted_unique_ops = sorted(unique_ops, key=operator_order) return sorted_unique_ops
def extract_operator_products(e, independent=False): """ Return a list of unique normal-ordered quantum operator products in the expression e. """ ops = [] if isinstance(e, Operator): ops.append(e) elif isinstance(e, Add): for arg in e.args: ops += extract_operator_products(arg, independent=independent) elif isinstance(e, Mul): c, o = split_coeff_operator(e) if o != 1: ops.append(o) else: if debug: print("Unrecongized type: %s: %s" % (type(e), str(e))) no_ops = [] for op in ops: no_op = normal_ordered_form(op.expand(), independent=independent) if isinstance(no_op, (Mul, Operator, Pow)): no_ops.append(no_op) elif isinstance(no_op, Add): for sub_no_op in extract_operator_products(no_op, independent=independent): no_ops.append(sub_no_op) else: raise ValueError("Unsupported type in loop over ops: %s: %s" % (type(no_op), no_op)) return list(set(no_ops))
def bch_expansion(A, B, N=6, collect_operators=None, independent=False, expansion_search=True): # Use BCH expansion of order N if debug: print("bch_expansion: ", A, B) c, _ = split_coeff_operator(A) if debug: print("A coefficient: ", c) if debug: print("bch_expansion: ") e_bch = _bch_expansion(A, B, N=N).doit(independent=independent) if debug: print("simplify: ") e = qsimplify( normal_ordered_form(e_bch.expand(), recursive_limit=25, independent=independent).expand()) if debug: print("extract operators: ") ops = extract_operator_products(e, independent=independent) # make sure that product operators comes first in the list ops = list(reversed(sorted(ops, key=lambda x: len(str(x))))) if debug: print("operators in expression: ", ops) if collect_operators: e_collected = collect(e, collect_operators) else: e_collected = collect(e, ops) if debug: print("search for series expansions: ", expansion_search) if expansion_search and c: return _expansion_search(e_collected, c, N) else: return e_collected
def qsimplify(e_orig, _n=0): """ Simplify an expression containing operators. """ if _n > 15: warnings.warn("Too high level or recursion, aborting") return e_orig e = normal_ordered_form(e_orig) if isinstance(e, Add): return Add(*(qsimplify(arg, _n=_n+1) for arg in e.args)) elif isinstance(e, Pow): return Pow(*(qsimplify(arg, _n=_n+1) for arg in e.args)) elif isinstance(e, exp): return exp(*(qsimplify(arg, _n=_n+1) for arg in e.args)) elif isinstance(e, Mul): args1 = tuple(arg for arg in e.args if arg.is_commutative) args2 = tuple(arg for arg in e.args if not arg.is_commutative) # x = 1 # for y in args2: # x = x * y x = 1 for y in reversed(args2): x = y * x if isinstance(x, Mul): args2 = x.args x = 1 for y in args2: x = x * y e_new = simplify(Mul(*args1)) * x if e_new == e: return e else: return qsimplify(e_new.expand(), _n=_n+1) if e == e_orig: return e else: return qsimplify(e, _n=_n+1).expand()
def qsimplify(e_orig, _n=0): """ Simplify an expression containing operators. """ if _n > 15: warnings.warn("Too high level or recursion, aborting") return e_orig e = normal_ordered_form(e_orig) if isinstance(e, Add): return Add(*(qsimplify(arg, _n=_n + 1) for arg in e.args)) elif isinstance(e, Pow): return Pow(*(qsimplify(arg, _n=_n + 1) for arg in e.args)) elif isinstance(e, exp): return exp(*(qsimplify(arg, _n=_n + 1) for arg in e.args)) elif isinstance(e, Mul): args1 = tuple(arg for arg in e.args if arg.is_commutative) args2 = tuple(arg for arg in e.args if not arg.is_commutative) #x = 1 #for y in args2: # x = x * y x = 1 for y in reversed(args2): x = y * x if isinstance(x, Mul): args2 = x.args x = 1 for y in args2: x = x * y e_new = simplify(Mul(*args1)) * x if e_new == e: return e else: return qsimplify(e_new.expand(), _n=_n + 1) if e == e_orig: return e else: return qsimplify(e, _n=_n + 1).expand()
def extract_all_operators(e_orig): """ Extract all unique operators in the normal ordered for of a given operator expression, including composite operators. The resulting list of operators are sorted in increasing order. """ if debug: print("extract_all_operators: ", e_orig) if isinstance(e_orig, Operator): return [e_orig] e = drop_c_number_terms( normal_ordered_form(e_orig.expand(), independent=True)) if isinstance(e, Pow) and isinstance(e.base, Operator): return [e] ops = [] if isinstance(e, Add): for arg in e.args: ops += extract_all_operators(arg) if isinstance(e, Mul): op_f = [ f for f in e.args if (isinstance(f, Operator) or ( isinstance(f, Pow) and isinstance(f.base, Operator))) ] ops.append(Mul(*op_f)) ops += op_f unique_ops = list(set(ops)) sorted_unique_ops = sorted(unique_ops, key=operator_order) return sorted_unique_ops
def extract_operator_products(e, independent=False): """ Return a list of unique normal-ordered quantum operator products in the expression e. """ ops = [] if isinstance(e, Operator): ops.append(e) elif isinstance(e, Add): for arg in e.args: ops += extract_operator_products(arg, independent=independent) elif isinstance(e, Mul): c, o = split_coeff_operator(e) if o != 1: ops.append(o) else: if debug: print("Unrecongized type: %s: %s" % (type(e), str(e))) no_ops = [] for op in ops: no_op = normal_ordered_form(op.expand(), independent=independent) if isinstance(no_op, (Mul, Operator, Pow)): no_ops.append(no_op) elif isinstance(no_op, Add): for sub_no_op in extract_operator_products( no_op, independent=independent): no_ops.append(sub_no_op) else: raise ValueError("Unsupported type in loop over ops: %s: %s" % (type(no_op), no_op)) return list(set(no_ops))
def bch_expansion(A, B, N=6, collect_operators=None, independent=False, expansion_search=True): # Use BCH expansion of order N if debug: print("bch_expansion: ", A, B) cno = split_coeff_operator(A) if isinstance(cno, list): nvar = len(cno) c_list = [] o_list = [] for n in range(nvar): c_list.append(cno[n][0]) o_list.append(cno[n][1]) else: nvar = 1 c_list, o_list = [cno[0]], [cno[1]] if debug: print("A coefficient: ", c_list) rep_list = [] var_list = [] for n in range(nvar): rep_list.append(Dummy()) coeff, sym = c_list[n].as_coeff_Mul() if isinstance(sym, Mul): sym_ = simplify(sym) if I in sym_.args: var_list.append(sym_/I) elif any([isinstance(arg, exp) for arg in sym_.args]): nexps = Mul(*[arg for arg in sym_.args if not isinstance(arg, exp)]) exps = Mul(*[arg for arg in sym_.args if isinstance(arg, exp)]) if I in simplify(exps).exp.args: var_list.append(nexps) else: var_list.append(sym_) else: var_list.append(sym) A_rep = A.subs({var_list[n]: rep_list[n] for n in range(nvar)}) e_bch_rep = _bch_expansion(A_rep, B, N=N).doit(independent=independent) if debug: print("simplify: ") e = qsimplify(normal_ordered_form(e_bch_rep.expand(), recursive_limit=25, independent=independent).expand()) if debug: print("extract operators: ") ops = extract_operator_products(e, independent=independent) # make sure that product operators comes first in the list ops = list(reversed(sorted(ops, key=lambda x: len(str(x))))) if debug: print("operators in expression: ", ops) if collect_operators: e_collected = collect(e, collect_operators) else: e_collected = collect(e, ops) if debug: print("search for series expansions: ", expansion_search) if debug: print("e_collected: ", e_collected) if expansion_search and c_list: e_collected = _expansion_search(e_collected, rep_list, N) e_collected = e_collected.subs({rep_list[n]: var_list[n] for n in range(nvar)}) return e_collected else: return e_collected.subs( {rep_list[n]: var_list[n] for n in range(nvar)})
def semi_classical_eqm(H, c_ops, N=20, discard_unresolved=True): """ Generate a set of semiclassical equations of motion from a Hamiltonian and set of collapse operators. Equations of motion for all operators that are included in either the Hamiltonian or the list of collapse operators will be generated, as well as any operators that are included in the equations of motion for the orignal operators. If the system of equations for the averages of operators does not close, the highest order operators will be truncated and removed from the equations. """ op_eqm = {} ops = extract_all_operators(H + sum(c_ops)) if debug: print("Hamiltonian operators: ", ops) t = symbols("t", positive=True) n = 0 while ops: if n > N: break n += 1 _, idx = min((val, idx) for (idx, val) in enumerate([operator_order(op) for op in ops])) op = ops.pop(idx) lhs, rhs = operator_master_equation(op, t, H, c_ops, use_eq=False) op_eqm[op] = qsimplify(normal_ordered_form( rhs.doit(independent=True).expand(), independent=True)) new_ops = extract_all_operators(op_eqm[op]) for new_op in new_ops: if ((not new_op.is_Number) and new_op not in op_eqm.keys() and new_op not in ops): if debug: print(new_op, "not included, adding") ops.append(new_op) if debug: print("unresolved ops: ", ops) if discard_unresolved: for op, eqm in op_eqm.items(): op_eqm[op] = drop_terms_containing(op_eqm[op], ops) ops_unresolved = [] else: ops_unresolved = ops #for op, eqm in op_eqm.items(): # for o in extract_all_operators(eqm): # if o not in op_eqm.keys(): # print("unresolved operator: %r" % o) op_factorization = {} sc_eqm = {} for op, eqm in op_eqm.items(): ops = extract_all_operators(eqm) sc_eqm[op] = Expectation(eqm).expand(expectation=True) for op2 in ops_unresolved: if op2 not in op_eqm.keys(): # need to factor op2 sub_ops = _sceqm_factor_op(op2, op_eqm.keys()) factored_expt = Mul(*(Expectation(o) for o in sub_ops)) op_factorization[Expectation(op2)] = factored_expt sc_eqm[op] = sc_eqm[op].subs(Expectation(op2), factored_expt) op_func_map = {} op_index_map = {} for n, op in enumerate(op_eqm): op_func_map[op] = Function("A%d" % n)(t) op_index_map[op] = n if debug: print("Operator -> Function map: ", op_func_map) sc_ode = {} for op, eqm in sc_eqm.items(): sc_ode[op] = Eq(Derivative(_operator_to_func(Expectation(op), op_func_map), t), _operator_to_func(eqm, op_func_map)) ops = operator_sort_by_order(op_func_map.keys()) #for eqm in op_eqm: # eqm_ops = extract_all_operators(op_eqm[op]) SemiClassicalEQM = namedtuple('SemiClassicalEQM', ['operators', 'operators_unresolved', 'operator_eqs', 'operator_factorization', 'sc_eqs', 'sc_ode', 'op_func_map', 'op_index_map', 't' ]) return SemiClassicalEQM(ops, ops_unresolved, op_eqm, op_factorization, sc_eqm, sc_ode, op_func_map, op_index_map, t)
def semi_classical_eqm(H, c_ops, N=20, discard_unresolved=True): """ Generate a set of semiclassical equations of motion from a Hamiltonian and set of collapse operators. Equations of motion for all operators that are included in either the Hamiltonian or the list of collapse operators will be generated, as well as any operators that are included in the equations of motion for the orignal operators. If the system of equations for the averages of operators does not close, the highest order operators will be truncated and removed from the equations. """ op_eqm = {} ops = extract_all_operators(H + sum(c_ops)) if debug: print("Hamiltonian operators: ", ops) t = symbols("t", positive=True) n = 0 while ops: if n > N: break n += 1 _, idx = min( (val, idx) for (idx, val) in enumerate([operator_order(op) for op in ops])) op = ops.pop(idx) lhs, rhs = operator_master_equation(op, t, H, c_ops, use_eq=False) op_eqm[op] = qsimplify( normal_ordered_form(rhs.doit(independent=True).expand(), independent=True)) new_ops = extract_all_operators(op_eqm[op]) for new_op in new_ops: if ((not new_op.is_Number) and new_op not in op_eqm.keys() and new_op not in ops): if debug: print(new_op, "not included, adding") ops.append(new_op) if debug: print("unresolved ops: ", ops) if discard_unresolved: for op, eqm in op_eqm.items(): op_eqm[op] = drop_terms_containing(op_eqm[op], ops) ops_unresolved = [] else: ops_unresolved = ops #for op, eqm in op_eqm.items(): # for o in extract_all_operators(eqm): # if o not in op_eqm.keys(): # print("unresolved operator: %r" % o) op_factorization = {} sc_eqm = {} for op, eqm in op_eqm.items(): ops = extract_all_operators(eqm) sc_eqm[op] = Expectation(eqm).expand(expectation=True) for op2 in ops_unresolved: if op2 not in op_eqm.keys(): # need to factor op2 sub_ops = _sceqm_factor_op(op2, op_eqm.keys()) factored_expt = Mul(*(Expectation(o) for o in sub_ops)) op_factorization[Expectation(op2)] = factored_expt sc_eqm[op] = sc_eqm[op].subs(Expectation(op2), factored_expt) op_func_map = {} op_index_map = {} for n, op in enumerate(op_eqm): op_func_map[op] = Function("A%d" % n)(t) op_index_map[op] = n if debug: print("Operator -> Function map: ", op_func_map) sc_ode = {} for op, eqm in sc_eqm.items(): sc_ode[op] = Eq( Derivative(_operator_to_func(Expectation(op), op_func_map), t), _operator_to_func(eqm, op_func_map)) ops = operator_sort_by_order(op_func_map.keys()) #for eqm in op_eqm: # eqm_ops = extract_all_operators(op_eqm[op]) SemiClassicalEQM = namedtuple('SemiClassicalEQM', [ 'operators', 'operators_unresolved', 'operator_eqs', 'operator_factorization', 'sc_eqs', 'sc_ode', 'op_func_map', 'op_index_map', 't' ]) return SemiClassicalEQM(ops, ops_unresolved, op_eqm, op_factorization, sc_eqm, sc_ode, op_func_map, op_index_map, t)
def bch_expansion(A, B, N=6, collect_operators=None, independent=False, expansion_search=True): # Use BCH expansion of order N if debug: print("bch_expansion: ", A, B) cno = split_coeff_operator(A) if isinstance(cno, list): nvar = len(cno) c_list = [] o_list = [] for n in range(nvar): c_list.append(cno[n][0]) o_list.append(cno[n][1]) else: nvar = 1 c_list, o_list = [cno[0]], [cno[1]] if debug: print("A coefficient: ", c_list) rep_list = [] var_list = [] for n in range(nvar): rep_list.append(Dummy()) coeff, sym = c_list[n].as_coeff_Mul() if isinstance(sym, Mul): sym_ = simplify(sym) if I in sym_.args: var_list.append(sym_ / I) elif any([isinstance(arg, exp) for arg in sym_.args]): nexps = Mul( *[arg for arg in sym_.args if not isinstance(arg, exp)]) exps = Mul(*[arg for arg in sym_.args if isinstance(arg, exp)]) if I in simplify(exps).exp.args: var_list.append(nexps) else: var_list.append(sym_) else: var_list.append(sym) A_rep = A.subs({var_list[n]: rep_list[n] for n in range(nvar)}) e_bch_rep = _bch_expansion(A_rep, B, N=N).doit(independent=independent) if debug: print("simplify: ") e = qsimplify( normal_ordered_form(e_bch_rep.expand(), recursive_limit=25, independent=independent).expand()) if debug: print("extract operators: ") ops = extract_operator_products(e, independent=independent) # make sure that product operators comes first in the list ops = list(reversed(sorted(ops, key=lambda x: len(str(x))))) if debug: print("operators in expression: ", ops) if collect_operators: e_collected = collect(e, collect_operators) else: e_collected = collect(e, ops) if debug: print("search for series expansions: ", expansion_search) if debug: print("e_collected: ", e_collected) if expansion_search and c_list: e_collected = _expansion_search(e_collected, rep_list, N) e_collected = e_collected.subs( {rep_list[n]: var_list[n] for n in range(nvar)}) return e_collected else: return e_collected.subs( {rep_list[n]: var_list[n] for n in range(nvar)})
def bch_expansion(A, B, N=6, collect_operators=None, independent=False, expansion_search=True): """ Apply the BCH expansion using the exponent A and the operator B """ # Use BCH expansion of order N if debug: print("bch_expansion: ", A, B) # Split the terms (of the summation) into tuples of variables and operators cno = split_coeff_operator(A) if isinstance(cno, list): nvar = len(cno) c_list = [] o_list = [] for n in range(nvar): c_list.append(cno[n][0]) o_list.append(cno[n][1]) else: nvar = 1 c_list, o_list = [cno[0]], [cno[1]] if debug: print("A coefficient: ", c_list) rep_list = [] var_list = [] # Extract the parts of the exponent expression that contains symbols only... # 3*theta*I*sx + b*I*phi*sy => [theta, b*phi] # b*exp(-phi*sy) => [b*exp(-phi*SigmaY())] for n in range(nvar): rep_list.append(Dummy()) # Split multiplication into Coefficient (Number) and an equation of Symbols (only) coeff, sym = c_list[n].as_coeff_Mul() if isinstance(sym, Mul): sym_ = simplify(sym) if I in sym_.args: var_list.append(sym_ / I) elif any([isinstance(arg, exp) for arg in sym_.args]): # In case there are exponentials in symbols equation, split it into a part with # and a part without exponentials nexps = Mul( *[arg for arg in sym_.args if not isinstance(arg, exp)]) exps = Mul(*[arg for arg in sym_.args if isinstance(arg, exp)]) # I don't follow this.... if I in simplify(exps).exp.args: var_list.append(nexps) else: var_list.append(sym_) else: var_list.append(sym) A_rep = A.subs({var_list[n]: rep_list[n] for n in range(nvar)}) e_bch_rep = _bch_expansion(A_rep, B, N=N).doit(independent=independent) if debug: print("simplify: ") e = qsimplify( normal_ordered_form(e_bch_rep.expand(), recursive_limit=25, independent=independent).expand()) if debug: print("extract operators: ") if collect_operators: ops = collect_operators else: ops = extract_operator_products(e, independent=independent) # make sure that product operators comes first in the list ops = list(reversed(sorted(ops, key=lambda x: len(str(x))))) if debug: print("operators in expression: ", ops) # Apply hack to pass aroung collect to handling non-commuting symbols.... e_collected = ncollect(e, *ops) # reg = {} # for op in ops: # dummy = Dummy() # e = e.subs(op, dummy) # reg[dummy] = op # e_collected = collect(e, reg.keys()) # e_collected = e_collected.subs(reg) if debug: print("search for series expansions: ", expansion_search) if debug: print("e_collected: ", e_collected) if expansion_search and c_list: e_collected = _expansion_search(e_collected, rep_list) e_collected = e_collected.subs( {rep_list[n]: var_list[n] for n in range(nvar)}) return e_collected else: return e_collected.subs( {rep_list[n]: var_list[n] for n in range(nvar)})