def lr_op(left, right): """Perform a LR operation. A LR operation multiplies both left and right circuits with the dagger of the left circuit's rightmost gate, and the dagger is multiplied on the right side of both circuits. If a LR is possible, it returns the new gate rule as a 2-tuple (LHS, RHS), where LHS is the left circuit and and RHS is the right circuit of the new rule. If a LR is not possible, None is returned. Parameters ========== left : Gate tuple The left circuit of a gate rule expression. right : Gate tuple The right circuit of a gate rule expression. Examples ======== Generate a new gate rule using a LR operation: >>> from sympy.physics.quantum.identitysearch import lr_op >>> from sympy.physics.quantum.gate import X, Y, Z >>> x = X(0); y = Y(0); z = Z(0) >>> lr_op((x, y, z), ()) ((X(0), Y(0)), (Z(0),)) >>> lr_op((x, y), (z,)) ((X(0),), (Z(0), Y(0))) """ if (len(left) > 0): lr_gate = left[len(left) - 1] lr_gate_is_unitary = is_scalar_matrix((Dagger(lr_gate), lr_gate), _get_min_qubits(lr_gate), True) if (len(left) > 0 and lr_gate_is_unitary): # Get the new left side w/o the rightmost gate new_left = left[0:len(left) - 1] # Add the rightmost gate to the right position on the right side new_right = right + (Dagger(lr_gate), ) # Return the new gate rule return (new_left, new_right) return None
def _process_monomial(self, monomial, n_vars): """Process a single monomial when building the moment matrix. """ coeff, monomial = monomial.as_coeff_Mul() k = 0 # Have we seen this monomial before? conjugate = False try: # If yes, then we improve sparsity by reusing the # previous variable to denote this entry in the matrix k = self.monomial_index[monomial] except KeyError: # An extra round of substitutions is granted on the conjugate of # the monomial if all the variables are Hermitian daggered_monomial = \ apply_substitutions(Dagger(monomial), self.substitutions, self.pure_substitution_rules) try: k = self.monomial_index[daggered_monomial] conjugate = True except KeyError: # Otherwise we define a new entry in the associated # array recording the monomials, and add an entry in # the moment matrix k = n_vars + 1 self.monomial_index[monomial] = k if conjugate: k = -k return k, coeff
def test_numpy_dagger(): if not np: skip("numpy not installed.") a = np.matrix([[1.0, 2.0j], [-1.0j, 2.0]]) adag = a.copy().transpose().conjugate() assert (Dagger(a) == adag).all()
def get_ncmonomials(variables, degree): """Generates all noncommutative monomials up to a degree :param variables: The noncommutative variables to generate monomials from :type variables: list of :class:`sympy.physics.quantum.operator.Operator` or :class:`sympy.physics.quantum.operator`. :param degree: The maximum degree. :type degree: int. :returns: list of monomials. """ if not variables: return [S.One] else: _variables = variables[:] _variables.insert(0, 1) ncmonomials = [S.One] for _ in range(degree): temp = [] for var in _variables: for new_var in ncmonomials: temp.append(var * new_var) if var != 1 and not var.is_hermitian: temp.append(Dagger(var) * new_var) ncmonomials = unique(temp[:]) return ncmonomials
def __process_monomial(self, monomial, n_vars): """Process a single monomial when building the moment matrix. """ if monomial.as_coeff_Mul()[0] < 0: monomial = -monomial k = 0 # Have we seen this monomial before? try: # If yes, then we improve sparsity by reusing the # previous variable to denote this entry in the matrix k = self.monomial_index[monomial] except KeyError: # An extra round of substitutions is granted on the conjugate of # the monomial if all the variables are Hermitian need_new_variable = True if self.is_hermitian_variables and ncdegree(monomial) > 2: daggered_monomial = apply_substitutions( Dagger(monomial), self.substitutions) try: k = self.monomial_index[daggered_monomial] need_new_variable = False except KeyError: need_new_variable = True if need_new_variable: # Otherwise we define a new entry in the associated # array recording the monomials, and add an entry in # the moment matrix k = n_vars + 1 self.monomial_index[monomial] = k return k
def test_tensorproduct(): a = BosonOp("a") b = BosonOp("b") ket1 = TensorProduct(BosonFockKet(1), BosonFockKet(2)) ket2 = TensorProduct(BosonFockKet(0), BosonFockKet(0)) ket3 = TensorProduct(BosonFockKet(0), BosonFockKet(2)) bra1 = TensorProduct(BosonFockBra(0), BosonFockBra(0)) bra2 = TensorProduct(BosonFockBra(1), BosonFockBra(2)) assert qapply(TensorProduct(a, b**2) * ket1) == sqrt(2) * ket2 assert qapply(TensorProduct(a, Dagger(b) * b) * ket1) == 2 * ket3 assert qapply(bra1 * TensorProduct(a, b * b), dagger=True) == sqrt(2) * bra2 assert qapply(bra2 * ket1).doit() == TensorProduct(1, 1) assert qapply(TensorProduct(a, b * b) * ket1) == sqrt(2) * ket2 assert qapply(Dagger(TensorProduct(a, b * b) * ket1), dagger=True) == sqrt(2) * Dagger(ket2)
def __get_index_of_monomial(self, element, enablesubstitution=True): """Returns the index of a monomial. """ monomial, coeff = build_monomial(element) if enablesubstitution: monomial = apply_substitutions(monomial, self.substitutions) # Given the monomial, we need its mapping L_y(w) to push it into # a corresponding constraint matrix if monomial != 0: if monomial.as_coeff_Mul()[0] < 0: monomial = -monomial coeff = -1.0 * coeff k = -1 if monomial.is_Number: k = 0 else: try: k = self.monomial_index[monomial] except KeyError: try: [monomial, coeff] = build_monomial(element) monomial, scalar_factor = separate_scalar_factor( apply_substitutions(Dagger(monomial), self.substitutions)) coeff *= scalar_factor k = self.monomial_index[monomial] except KeyError: # An extra round of substitutions is granted on the # conjugate of the monomial if all the variables are #Hermitian exists = False if self.is_hermitian_variables: daggered_monomial = \ apply_substitutions(Dagger(monomial), self.substitutions) try: k = self.monomial_index[daggered_monomial] exists = True except KeyError: exists = False if not exists and self.verbose > 0: [monomial, coeff] = build_monomial(element) sub = apply_substitutions(Dagger(monomial), self.substitutions) print(("DEBUG: %s, %s, %s" % (element, Dagger(monomial), sub))) return k, coeff
def _generate_outer_prod(self, arg1, arg2): c_part1, nc_part1 = arg1.args_cnc() c_part2, nc_part2 = arg2.args_cnc() if (len(nc_part1) == 0 or len(nc_part2) == 0): raise ValueError('Atleast one-pair of' ' Non-commutative instance required' ' for outer product.') # Muls of Tensor Products should be expanded # before this function is called if (isinstance(nc_part1[0], TensorProduct) and len(nc_part1) == 1 and len(nc_part2) == 1): op = tensor_product_simp(nc_part1[0] * Dagger(nc_part2[0])) else: op = Mul(*nc_part1) * Dagger(Mul(*nc_part2)) return Mul(*c_part1) * Mul(*c_part2) * op
def bosonic_constraints(a): """Return a set of constraints that define fermionic ladder operators. :param a: The non-Hermitian variables. :type a: list of :class:`sympy.physics.quantum.operator.Operator`. :returns: a dict of substitutions. """ substitutions = {} for i, ai in enumerate(a): substitutions[ai * Dagger(ai)] = 1.0 + Dagger(ai) * ai for aj in a[i + 1:]: # substitutions[ai*Dagger(aj)] = -Dagger(ai)*aj substitutions[ai * Dagger(aj)] = Dagger(aj) * ai substitutions[Dagger(ai) * aj] = aj * Dagger(ai) substitutions[ai * aj] = aj * ai substitutions[Dagger(ai) * Dagger(aj)] = Dagger(aj) * Dagger(ai) return substitutions
def test_ground_state_energy(self): N = 3 a = generate_operators('a', N) substitutions = bosonic_constraints(a) hamiltonian = sum(Dagger(a[i]) * a[i] for i in range(N)) sdpRelaxation = SdpRelaxation(a, verbose=0) sdpRelaxation.get_relaxation(1, objective=hamiltonian, substitutions=substitutions) sdpRelaxation.solve() self.assertTrue(abs(sdpRelaxation.primal) < 10e-5)
def test_scipy_sparse_dagger(): if not np: skip("numpy not installed.") if not scipy: skip("scipy not installed.") else: sparse = scipy.sparse a = sparse.csr_matrix([[1.0 + 0.0j, 2.0j], [-1.0j, 2.0 + 0.0j]]) adag = a.copy().transpose().conjugate() assert np.linalg.norm((Dagger(a) - adag).todense()) == 0.0
def test_identity(): I = IdentityOperator() O = Operator('O') x = Symbol("x") assert isinstance(I, IdentityOperator) assert isinstance(I, Operator) assert I * O == O assert O * I == O assert I * Dagger(O) == Dagger(O) assert Dagger(O) * I == Dagger(O) assert isinstance(I * I, IdentityOperator) assert isinstance(3 * I, Mul) assert isinstance(I * x, Mul) assert I.inv() == I assert Dagger(I) == I assert qapply(I * O) == O assert qapply(O * I) == O for n in [2, 3, 5]: assert represent(IdentityOperator(n)) == eye(n)
def __generate_moment_matrix(self, n_vars, block_index, monomialsA, monomialsB): """Generate the moment matrix of monomials. Arguments: n_vars -- current number of variables block_index -- current block index in the SDP matrix monomials -- |W_d| set of words of length up to the relaxation level """ row_offset = 0 if block_index > 0: for block_size in self.block_struct[0:block_index]: row_offset += block_size**2 N = len(monomialsA) * len(monomialsB) # We process the M_d(u,w) entries in the moment matrix for rowA in range(len(monomialsA)): for columnA in range(rowA, len(monomialsA)): for rowB in range(len(monomialsB)): start_columnB = 0 if rowA == columnA: start_columnB = rowB for columnB in range(start_columnB, len(monomialsB)): monomial = Dagger(monomialsA[rowA]) * \ monomialsA[columnA] * \ Dagger(monomialsB[rowB]) * \ monomialsB[columnB] # Apply the substitutions if any n_vars = self.__push_monomial(monomial, n_vars, row_offset, rowA, columnA, N, rowB, columnB, len(monomialsB)) if self.verbose > 0: sys.stdout.write( "\r\x1b[KCurrent number of SDP variables: %d" % n_vars) sys.stdout.flush() if self.verbose > 0: sys.stdout.write("\r") return n_vars, block_index + 1
def test_identity(): I = IdentityOperator() O = Operator('O') assert isinstance(I, IdentityOperator) assert isinstance(I, Operator) assert I.inv() == I assert Dagger(I) == I assert qapply(I * O) == O assert qapply(O * I) == O for n in [2, 3, 5]: assert represent(IdentityOperator(n)) == eye(n)
def __process_inequalities(self, inequalities, block_index): """Generate localizing matrices Arguments: inequalities -- list of inequality constraints monomials -- localizing monomials block_index -- the current block index in constraint matrices of the SDP relaxation """ all_monomials = flatten(self.monomial_sets) initial_block_index = block_index row_offsets = [0] for block, block_size in enumerate(self.block_struct): row_offsets.append(row_offsets[block] + block_size**2) for k, ineq in enumerate(inequalities): block_index += 1 localization_order = self.localization_order[block_index - initial_block_index - 1] if self.hierarchy == "npa_chordal": index = find_clique_index(self.variables, ineq, self.clique_set) monomials = \ pick_monomials_up_to_degree(self.monomial_sets[index], localization_order) else: monomials = \ pick_monomials_up_to_degree(all_monomials, localization_order) monomials = unique(monomials) # Process M_y(gy)(u,w) entries for row in range(len(monomials)): for column in range(row, len(monomials)): # Calculate the moments of polynomial entries polynomial = \ simplify_polynomial( Dagger(monomials[row]) * ineq * monomials[column], self.substitutions) self.__push_facvar_sparse(polynomial, block_index, row_offsets[block_index - 1], row, column) if self.verbose > 0: sys.stdout.write("\r\x1b[KProcessing %d/%d constraints..." % (k + 1, len(inequalities))) sys.stdout.flush() if self.verbose > 0: sys.stdout.write("\n") return block_index
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 test_outer_product(): k = Ket('k') b = Bra('b') op = OuterProduct(k, b) assert isinstance(op, OuterProduct) assert isinstance(op, Operator) assert op.ket == k assert op.bra == b assert op.label == (k, b) assert op.is_commutative is False op = k * b assert isinstance(op, OuterProduct) assert isinstance(op, Operator) assert op.ket == k assert op.bra == b assert op.label == (k, b) assert op.is_commutative is False op = 2 * k * b assert op == Mul(Integer(2), k, b) op = 2 * (k * b) assert op == Mul(Integer(2), OuterProduct(k, b)) assert Dagger(k * b) == OuterProduct(Dagger(b), Dagger(k)) assert Dagger(k * b).is_commutative is False #test the _eval_trace assert Tr(OuterProduct(JzKet(1, 1), JzBra(1, 1))).doit() == 1
def test_dagger(): x = symbols("x") expr = Dagger(x) assert str(expr) == "Dagger(x)" ascii_str = """\ +\n\ x \ """ ucode_str = u("""\ †\n\ x \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str assert latex(expr) == r"x^{\dagger}" sT(expr, "Dagger(Symbol('x'))")
def test_bra_ket_dagger(): x = symbols('x', complex=True) k = Ket('k') b = Bra('b') assert Dagger(k) == Bra('k') assert Dagger(b) == Ket('b') assert Dagger(k).is_commutative == False k2 = Ket('k2') e = 2 * I * k + x * k2 assert Dagger(e) == conjugate(x) * Dagger(k2) - 2 * I * Dagger(k)
def test_bra_ket_dagger(): x = symbols("x", complex=True) k = Ket("k") b = Bra("b") assert Dagger(k) == Bra("k") assert Dagger(b) == Ket("b") assert Dagger(k).is_commutative is False k2 = Ket("k2") e = 2 * I * k + x * k2 assert Dagger(e) == conjugate(x) * Dagger(k2) - 2 * I * Dagger(k)
def test_dagger(): x = symbols('x') expr = Dagger(x) assert str(expr) == 'Dagger(x)' ascii_str = \ """\ +\n\ x \ """ ucode_str = \ u"""\ †\n\ x \ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str assert latex(expr) == r'x^{\dag}' sT(expr, "Dagger(Symbol('x'))")
def __process_equalities(self, equalities, all_monomials): """Generate localizing matrices Arguments: equalities -- list of equality constraints monomials -- localizing monomials level -- the level of the relaxation """ max_localization_order = 0 for equality in equalities: # Find the order of the localizing matrix eq_order = ncdegree(equality) if eq_order > 2 * self.level: print(("An equality constraint has degree %d. Choose a higher \ level of relaxation." % eq_order)) raise Exception localization_order = int(floor((2 * self.level - eq_order) / 2)) if localization_order > max_localization_order: max_localization_order = localization_order monomials = \ pick_monomials_up_to_degree(all_monomials, max_localization_order) A = np.zeros( (len(equalities) * len(monomials) * (len(monomials) + 1) / 2, self.n_vars + 1)) n_rows = 0 for equality in equalities: # Find the order of the localizing matrix # Process M_y(gy)(u,w) entries for row in range(len(monomials)): for column in range(row, len(monomials)): # Calculate the moments of polynomial entries polynomial = \ simplify_polynomial(Dagger(monomials[row]) * equality * monomials[column], self.substitutions) A[n_rows] = self.__get_facvar(polynomial) # This is something really weird: we want the constant # terms in equalities to be positive. Otherwise funny # things happen in the QR decomposition and the basis # transformation. if A[n_rows, 0] < 0: A[n_rows] = -A[n_rows] n_rows += 1 return A
def doit(self, **hints): """Expand the density operator into an outer product format. Examples ========= >>> from sympy.physics.quantum.state import Ket >>> from sympy.physics.quantum.density import Density >>> from sympy.physics.quantum.operator import Operator >>> A = Operator('A') >>> d = Density([Ket(0), 0.5], [Ket(1),0.5]) >>> d.doit() 0.5*|0><0| + 0.5*|1><1| """ terms = [] for (state, prob) in self.args: terms.append(prob * (state * Dagger(state))) return Add(*terms)
def test_apply_substitutions(self): def apply_correct_substitutions(monomial, substitutions): if isinstance(monomial, int) or isinstance(monomial, float): return monomial original_monomial = monomial changed = True while changed: for lhs, rhs in substitutions.items(): monomial = monomial.subs(lhs, rhs) if original_monomial == monomial: changed = False original_monomial = monomial return monomial length, h, U, t = 2, 3.8, -6, 1 fu = generate_operators('fu', length) fd = generate_operators('fd', length) _b = flatten([fu, fd]) hamiltonian = 0 for j in range(length): hamiltonian += U * (Dagger(fu[j]) * Dagger(fd[j]) * fd[j] * fu[j]) hamiltonian += -h / 2 * (Dagger(fu[j]) * fu[j] - Dagger(fd[j]) * fd[j]) for k in get_neighbors(j, len(fu), width=1): hamiltonian += -t * Dagger(fu[j]) * fu[k] - t * Dagger( fu[k]) * fu[j] hamiltonian += -t * Dagger(fd[j]) * fd[k] - t * Dagger( fd[k]) * fd[j] substitutions = fermionic_constraints(_b) monomials = expand(hamiltonian).as_coeff_mul()[1][0].as_coeff_add()[1] substituted_hamiltonian = sum([ apply_substitutions(monomial, substitutions) for monomial in monomials ]) correct_hamiltonian = sum([ apply_correct_substitutions(monomial, substitutions) for monomial in monomials ]) self.assertTrue(substituted_hamiltonian == expand(correct_hamiltonian))
def test_scalars(): x = symbols('x', complex=True) assert Dagger(x) == conjugate(x) assert Dagger(I * x) == -I * conjugate(x) i = symbols('i', real=True) assert Dagger(i) == i p = symbols('p') assert isinstance(Dagger(p), adjoint) i = Integer(3) assert Dagger(i) == i A = symbols('A', commutative=False) assert Dagger(A).is_commutative is False
def test_represent(): x, y = symbols('x y') d = Density([XKet(), 0.5], [PxKet(), 0.5]) assert (represent(0.5 * (PxKet() * Dagger(PxKet()))) + represent(0.5 * (XKet() * Dagger(XKet())))) == represent(d) # check for kets with expr in them d_with_sym = Density([XKet(x * y), 0.5], [PxKet(x * y), 0.5]) assert (represent(0.5*(PxKet(x*y)*Dagger(PxKet(x*y)))) + represent(0.5*(XKet(x*y)*Dagger(XKet(x*y))))) == \ represent(d_with_sym) # check when given explicit basis assert (represent(0.5*(XKet()*Dagger(XKet())), basis=PxOp()) + represent(0.5*(PxKet()*Dagger(PxKet())), basis=PxOp())) == \ represent(d, basis=PxOp())
def _represent(self, **options): """A default represent that uses the Ket's version.""" from sympy.physics.quantum.dagger import Dagger return Dagger(self.dual._represent(**options))
def _eval_adjoint(self): return TensorProduct(*[Dagger(i) for i in self.args])
def nsi_sympy_mat_mult( eps_scale_val, eps_prime_val, phi12_val, phi13_val, phi23_val, alpha1_val, alpha2_val, deltansi_val, ): """Sympy calculation of generalised matter Hamiltonian.""" # pylint: disable=invalid-name from sympy import ( cos, sin, Matrix, eye, I, re, im, Symbol, symbols, simplify, init_printing ) from sympy.physics.quantum.dagger import Dagger init_printing(use_unicode=True) phi12, phi13, phi23 = symbols('phi12 phi13 phi23', real=True) alpha1, alpha2 = symbols('alpha1 alpha2', real=True) eps_scale, eps_prime = symbols('eps_scale eps_prime', real=True) deltansi = Symbol('deltansi', real=True) Dmat = Matrix( [[eps_scale, 0, 0], [0, eps_prime, 0], [0, 0, 0]] ) Qrel = Matrix( [[cos(alpha1) + I * sin(alpha1), 0, 0], [0, cos(alpha2) + I * sin(alpha2), 0], [0, 0, cos(-(alpha1 + alpha2)) + I * sin(-(alpha1 + alpha2))]] ) R12 = Matrix( [[cos(phi12), sin(phi12), 0], [-sin(phi12), cos(phi12), 0], [0, 0, 1]] ) R13 = Matrix( [[cos(phi13), 0, sin(phi13)], [0, 1, 0], [-sin(phi13), 0, cos(phi13)]] ) R23_complex = Matrix( [[1, 0, 0], [0, cos(phi23), sin(phi23) * (cos(deltansi) + I * sin(-deltansi))], [0, -sin(phi23) * (cos(deltansi) + I * sin(deltansi)), cos(phi23)]] ) Umat = R12 * R13 * R23_complex tmp = Dagger(Umat) * Dagger(Qrel) tmp2 = Dmat * tmp tmp3 = Umat * tmp2 Hmat_sympy = Qrel * tmp3 # subtract constant * id Hmat_sympy_minus_mumu = Hmat_sympy - Hmat_sympy[1, 1] * eye(3) Hmat_sympy_minus_mumu[0, 0] = Hmat_sympy_minus_mumu[0, 0] - 1 eps_mat_sympy = Hmat_sympy_minus_mumu # simplify eps_mat_sympy_simpl = simplify(eps_mat_sympy) # evaluate eps_mat_sympy_eval = eps_mat_sympy_simpl.subs( [(eps_scale, eps_scale_val), (eps_prime, eps_prime_val), (phi12, phi12_val), (phi13, phi13_val), (phi23, phi23_val), (alpha1, alpha1_val), (alpha2, alpha2_val), (deltansi, deltansi_val)] ) # real part eps_mat_sympy_eval_re = re(eps_mat_sympy_eval) # imaginary part eps_mat_sympy_eval_im = im(eps_mat_sympy_eval) # complex numpy array return ( np.array(eps_mat_sympy_eval_re) + np.array(eps_mat_sympy_eval_im) * 1.j ).astype(CTYPE)
def _eval_dagger(self): return AntiCommutator(Dagger(self.args[0]), Dagger(self.args[1]))