#!/usr/bin/env python # coding: utf-8 # ## Say "Hello World" With Qubiter # The purpose of this notebook is to illustrate how to use Qubiter to simulate ( i.e., # predict the outcome of) a simple quantum circuit with a few basic gates # # > Below, we won't always give the precise definition of each gate. You can find the # precise analytical/numerical definition of all gates implemented by Qubiter in the document entitled `qubiter_rosetta_stone.pdf` included with the Qubiter distribution. # $\newcommand{\bra}[1]{\left\langle{#1}\right|}$ # $\newcommand{\ket}[1]{\left|{#1}\right\rangle}$ # test: $\bra{\psi}M\ket{\phi}$ # First change your working directory to the Qubiter directory in your computer, and add its path to the path environment variable. # In[1]: import os import sys print(os.getcwd()) os.chdir('../../') print(os.getcwd()) sys.path.insert(0,os.getcwd()) # Suppose you are anywhere in your home ~ directory, and qubiter has been installed somewhere accessible via the path environmental variable. You can find where qubiter is installed like this, in case you want to cd there. # In[2]: from qubiter.utilities_gen import find_path_to_qubiter # this method returns the absolute path to the py file where the method is defined path = find_path_to_qubiter() print(path) # In[3]: from qubiter.SEO_writer import * from qubiter.SEO_simulator import * from qubiter.StateVec import * from qubiter.SEO_MatrixProduct import * import numpy as np # Sometimes, we use the word "bit" to denote both qbits (qubits, quantum bits) or cbits (classical bits). # # Number of qubits is 4. # In[4]: num_qbits = 4 # Use a trivial circuit embedder that embeds 4 qubits into same 4 qubits. # In[5]: emb = CktEmbedder(num_qbits, num_qbits) # Open a writer, tell it where to write to. # We will use zero bit last (ZL) convention, which is the default for SEO_writer. # In[6]: file_prefix = 'hello_world_test' wr = SEO_writer(file_prefix, emb) # Write Pauli matrices $\sigma_X, \sigma_Y,\sigma_Z$ at position 2. # In[7]: wr.write_X(2) wr.write_Y(2) wr.write_Z(2) # old way of doing it, still works # wr.write_one_qbit_gate(2, OneQubitGate.sigx) # wr.write_one_qbit_gate(2, OneQubitGate.sigy) # wr.write_one_qbit_gate(2, OneQubitGate.sigz) # Write 1 qubit Hadamard matrix at position 3. # In[8]: wr.write_H(3) # old way of doing it, still works # wr.write_one_qbit_gate(3, OneQubitGate.had2) # Rotate qubit 2 by $\pi$ along directions x, y, z successively. # # > Note: We define $Ra(\theta) = exp(i\theta\sigma_a)$ for $a=X,Y,Z$. Others use # $Ra(\theta) = exp(-i\frac{\theta}{2}\sigma_a)$ instead. # # > Note: $\theta$ in $Ra(\theta)$ is inserted in radians, but shows # up in the English File in degrees. # In[9]: wr.write_Rx(2, np.pi) wr.write_Ry(2, np.pi) wr.write_Rz(2, np.pi) # old way of doing it, still works dir=1,2,3 # wr.write_one_qbit_gate(2, OneQubitGate.rot_ax,[np.pi, dir]) # Rotate qubit 1 along a non-axis direction $\hat{n}$ characterized by a list of 3 angles. # $R(\theta_1, \theta_2, \theta_3) = \exp(i[\theta_1 \sigma_X +\theta_2\sigma_Y+\theta_3\sigma_Z])$ # In[10]: wr.write_Rn(1, [np.pi, np.pi/2, np.pi/3]) # Definitions of S and T # # $S = diag[1, i] = diag[1, e^{i\frac{\pi}{2}}]$ # # $T = \sqrt{S}= diag[1, e^{i\frac{\pi}{4}}]$ # # Write $S, S^\dagger, T, T^\dagger$ at position=2. # # > These operations show up in the English File as `P1PH` and in the # Picture File as `@P`. That is because $P_1 = n =\ket{1}\bra{1} = diag(0, 1)$ and the operation # `P1PH` (i.e. $P_1$ Phase) by a phase angle $\theta$ equals the diagonal matrix $diag(1, e^{i\theta})$ # In[11]: wr.write_S(2) wr.write_S(2, herm=True) wr.write_T(2) wr.write_T(2, herm=True) # Write $CNOT = sigx(target\_pos)^{n(control\_pos)}$ with control_pos=3 and target_pos=1 # In[12]: wr.write_cnot(3, 1) # old way of doing it, still works # control_pos = 3 # target_pos = 1 # trols = Controls.new_single_trol(num_qbits, control_pos, kind=True) # wr.write_controlled_one_qbit_gate( # target_pos, trols, OneQubitGate.sigx) # At any point in the circuit, you can use a PRINT statement. This will print # on the console, immediately after you create # an object of the class SEO_simulator, a description of the state vector at that point in the circuit. # Various styles of description are pre-canned for your convenience, or # you can write your own. See use_PRINT() method of SEO_simulator class. # Let's use a PRINT statement now in the pre-canned style "ALL". # In[13]: wr.write_PRINT("ALL") # Swap qubits 1 and 3 # In[14]: wr.write_qbit_swap(1, 3) # Recall that # $P_1 = n = \ket{1}\bra{1}=diag(0, 1)$ and a P1 phase (P1PH) by $\theta$ is $diag(1, e^{i\theta})$. Write a singly controlled P1PH with control=c=3, target=t=1 and rads = pi/3. # This gate equals $e^{i*rads*n(t) n(c)}$. # In[15]: wr.write_c_P1PH(3, 1, rads=np.pi/3) # If rads=pi, c_P1PH equals $(-1)^{n(t)n(c)} = \sigma_Z(t)^{n(c)}$, # which is commonly called a controlled Z and denoted by Cz. Write a Cz with c=3 and t=1. # In[16]: wr.write_c_P1PH(3, 1) # rads=np.pi is default # Write a controlled rotation at qubit 0 in the Y direction, with a True (@) control at qubit 1, and a False (0) control at qubits 2 and 3. # In[17]: target_pos = 0 rads = 30*np.pi/180 ax = 2 # y axis trols = Controls(num_qbits, {1:True, 2:False, 3:False}) wr.write_controlled_one_qbit_gate( target_pos, trols, OneQubitGate.rot_ax, [rads, ax]) # Close English and Picture files. # In[18]: wr.close_files() # Look in files # # * ../io_folder/hello_world_test_4_eng.txt # * ../io_folder/hello_world_test_4_ZLpic.txt # # to see the quantum circuit that was generated. # Once the English and Picture files are generated, you can ask the writer object wr to print them for you on screen # In[19]: wr.print_eng_file(jup=True) # In[20]: wr.print_pic_file(jup=True) # You can ask wr for the path to the English and Picture files # In[21]: print(wr.get_eng_file_path()) # In[22]: print(wr.get_pic_file_path()) # You can generate a log file with an inventory of the English file by creating # an object of the SEO_reader class with the flag `write_log` set to True # In[23]: rdr = SEO_reader(file_prefix, num_qbits, write_log=True) # The following file was just created # # * ../io_folder/hello_world_test_4_log.txt # Once the log file is generated, you can ask the reader object rdr to print it for you on screen # In[24]: rdr.print_log_file() # You can ask rdr for the path to the log file # In[25]: print(rdr.get_log_file_path()) # Occasionally, especially for debugging purposes, you might want to display the # product of a SEO (sequence of elementary operations, sequence of gates) as a 2^num_qbits dimensional # unitary matrix. This can be done with the class SEO_MatrixProduct. Simply # creating an object of this class multiplies the SEO and stores the result # in its attribute `self.prod_arr`. Next we print that array for our example # In[26]: mp = SEO_MatrixProduct(file_prefix, num_qbits) print('product array=') print(np.array_str(mp.prod_arr, precision=2, suppress_small=True)) # Specify initial state vector for simulation. This example corresponds to $\ket{0}\ket{0}\ket{1}\ket{1}$. In ZL convention, last ket corresponds to bit 0. # In[27]: init_st_vec = StateVec.get_standard_basis_st_vec([0, 0, 1, 1], ZL=True) # Open a simulator. This automatically # calculates the final state vector for the quantum circuit in the English file subject to # the initial state vector that you give as input to the SEO_simulator constructor. # Note that the PRINT statement that we inserted into the English file, prints, as promised, # immediately after creating the SEO_simulator object. # In[28]: sim = SEO_simulator(file_prefix, num_qbits, init_st_vec) # Ask sim to print a description of final state vector # In[29]: sim.describe_st_vec_dict(print_st_vec=True, do_pp=True, omit_zero_amps=True, show_pp_probs=True) # The object sim of SEO_simulator, holds, at this point, the final state vector # for the evolution of the circuit subject to the initial state vector chosen. # You might want to sample the probability distribution defined # by that final state vector, and obtain counts of each observed multi-qubit state # for a given number of shots. This is the type # of output that a real qc device gives, albeit # our counts have no extrinsic noise. One can ask sim to simulate such counts as follows: # In[30]: counts = sim.get_counts(num_shots=100) print(counts) # And you can ask the Plotter class to plot those counts as follows: # In[31]: Plotter.plot_counts(counts) # In[ ]: