def generate_eqm(H, c_ops, t, independent=True, max_order=2, 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 = {} #get_ops = lambda expr: extract_operator_products( get_ops = lambda expr: extract_all_operators( normal_ordered_form(expr, independent=independent).doit().expand()) ops = get_ops(H + sum(c_ops)) if debug: print("Hamiltonian operators: ", ops) while ops: order, idx = min( (val, idx) for (idx, val) in enumerate([operator_order(op) for op in ops])) if order > max_order: print("Warning: system did not close with max_order =", max_order) break op = ops.pop(idx) lhs, rhs = operator_master_equation(op, t, H, c_ops, use_eq=False) op_eqm[op] = normal_ordered_form( rhs.doit(independent=independent).expand(), independent=independent) new_ops = get_ops(op_eqm[op]) for new_op in new_ops: if new_op not in op_eqm and new_op not in ops: if debug: print(new_op, "not included, adding") ops.append(new_op) unresolved_ops = ops if debug: print("unresolved ops: ", unresolved_ops) if discard_unresolved: for op, eqm in op_eqm.items(): op_eqm[op] = drop_terms_containing(eqm, unresolved_ops) # in python 3.6+ dictionaries are sorted op_eqm = {op: op_eqm[op] for op in operator_sort_by_order(op_eqm.keys())} return op_eqm, unresolved_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, nc = e.args_cnc() if nc: ops.append(Mul(*nc)) 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): no_ops += extract_operator_products(no_op, independent=independent) else: raise ValueError("Unsupported type in loop over ops: %s: %s" % (type(no_op), no_op)) return list(set(no_ops))
def test_normal_ordered_form(): a = BosonOp("a") c = FermionOp("c") assert normal_ordered_form(Dagger(a) * a) == Dagger(a) * a assert normal_ordered_form(a * Dagger(a)) == 1 + Dagger(a) * a assert normal_ordered_form(a**2 * Dagger(a)) == 2 * a + Dagger(a) * a**2 assert normal_ordered_form(a**3 * Dagger(a)) == 3 * a**2 + Dagger(a) * a**3 assert normal_ordered_form(Dagger(c) * c) == Dagger(c) * c assert normal_ordered_form(c * Dagger(c)) == 1 - Dagger(c) * c assert normal_ordered_form(c**2 * Dagger(c)) == Dagger(c) * c**2 assert normal_ordered_form(c**3 * Dagger(c)) == c**2 - Dagger(c) * c**3
def test_normal_ordered_form(): a = BosonOp('a') c = FermionOp('c') assert normal_ordered_form(Dagger(a) * a) == Dagger(a) * a assert normal_ordered_form(a * Dagger(a)) == 1 + Dagger(a) * a assert normal_ordered_form(a ** 2 * Dagger(a)) == \ 2 * a + Dagger(a) * a ** 2 assert normal_ordered_form(a ** 3 * Dagger(a)) == \ 3 * a ** 2 + Dagger(a) * a ** 3 assert normal_ordered_form(Dagger(c) * c) == Dagger(c) * c assert normal_ordered_form(c * Dagger(c)) == 1 - Dagger(c) * c assert normal_ordered_form(c ** 2 * Dagger(c)) == Dagger(c) * c ** 2 assert normal_ordered_form(c ** 3 * Dagger(c)) == \ c ** 2 - Dagger(c) * c ** 3
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 test_normal_ordered_form(): a = BosonOp('a') b = BosonOp('b') c = FermionOp('c') d = FermionOp('d') assert normal_ordered_form(Dagger(a) * a) == Dagger(a) * a assert normal_ordered_form(a * Dagger(a)) == 1 + Dagger(a) * a assert normal_ordered_form(a ** 2 * Dagger(a)) == \ 2 * a + Dagger(a) * a ** 2 assert normal_ordered_form(a ** 3 * Dagger(a)) == \ 3 * a ** 2 + Dagger(a) * a ** 3 assert normal_ordered_form(Dagger(c) * c) == Dagger(c) * c assert normal_ordered_form(c * Dagger(c)) == 1 - Dagger(c) * c assert normal_ordered_form(c ** 2 * Dagger(c)) == Dagger(c) * c ** 2 assert normal_ordered_form(c ** 3 * Dagger(c)) == \ c ** 2 - Dagger(c) * c ** 3
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 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_) 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): # Use BCH expansion of order N if debug: print("bch_expansion: A =", A, " B =", B) A = A.expand() if isinstance(A, Add): args = A.args else: args = [A] cnc = [arg.args_cnc() for arg in args] c_list, o_list = zip(*[(Mul(*c), Mul(*nc)) for c, nc in cnc]) if debug: print("A coefficient: ", c_list, " A operator: ", o_list) var_list = [] for coeff, sym in (c.as_coeff_Mul() for c in c_list): 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(*[a for a in sym_.args if not isinstance(a, exp)]) exps = Mul(*[a for a in sym_.args if isinstance(a, exp)]) if I in simplify(exps).exp.args: var_list.append(nexps) else: var_list.append(sym_) else: var_list.append(sym) rep_list = [Dummy() for _ in range(len(var_list))] A_rep = A.subs({var_list[i]: rep_list[i] for i in range(len(var_list))}) if debug: print("A_rep: ", A_rep) e_bch_rep = _bch_expansion(A_rep, B, N=N).doit(independent=independent) e = qsimplify( normal_ordered_form(e_bch_rep.expand(), recursive_limit=25, independent=independent).expand()) if debug: print("simplify: ", e) ops = extract_operator_products(e, independent=independent) if debug: print("extract operators: ", ops) # 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 = qcollect(e, collect_operators) else: e_collected = qcollect(e, ops) if debug: print("e_collected: ", e_collected) if debug: print("search for series expansions: ", expansion_search) if expansion_search and rep_list: e_collected = _expansion_search(e_collected, rep_list, N) return e_collected.subs( {rep_list[i]: var_list[i] for i in range(len(var_list))})
def semi_classical_eqm(H, c_ops, max_order=2, 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) while ops: order, idx = min( (val, idx) for (idx, val) in enumerate([operator_order(op) for op in ops])) if order > max_order: break 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)