# Qubits are subject to noise orginating from:
# temperature varations, stray magnetic fields, or operations
# on other qubits.
# Finding a way to mitigate this noise through 
# the encoding of our in a way that protects them from noise.
# for some roation R(theta), about some axis it is impossible
# to impent an angle theta with perfect accuracy,such thta you are
# sure that you are not accidentally implement something like
# theta + 0.000000001. That is to say there will always be
# a limit to the accuracy we can achieve.
# As this error adds up through operations the noise will always
# be larger than is tolerable.
# Therefore implementing rotations in a fault tolerant
# Quantum Computer, uses mutiple operations made of T and H
# Gates.
%matplotlib inline
# Importing standard Qiskit libraries and configuring account
from qiskit import QuantumCircuit, execute, Aer, IBMQ
from qiskit.compiler import transpile, assemble
from qiskit.tools.jupyter import *
from qiskit.visualization import *
# Loading your IBM Q account(s)
provider = IBMQ.load_account()

# The T-GATE:
# Is a rotation around the z-axis by theta = pi/4.
# and is expressed mathematically as Rz(pi/4) = e^(ipi/8Z)
#Ex:
# the default T gate rotating about the z-axis:
qc = QuantumCircuit(1)
qc.t(0)
qc.draw()

# the T-Gate rotating about the x-axis:
qc = QuantumCircuit(1)
qc.h(0)
qc.t(0)
qc.h(0)
qc.draw()

# Putting the two circuits above together we can make the gate:
# Rz(pi/4)Rx(pi/4).
# Bc this is a single-qubit gate,we can think of it as a 
# rotation around the Bloch Sphere. That is as a rotaion 
# About some axis. It is important to note that this angle
# will always be irrational. Due to its irrationality,
# the angles that result from different repetitions will never
# be the same. Each angle will be somewhere between 0 & 2pi.
# Splitting this interval up into n slices of width 2pi/n.
# For eacg repetition, the resulting angle will fall in one
# of these slices. looking at the angle for the first n+1 
# repitioins, it must be true that at least one slicce contains
# two of these angles. Using n1 to denote the number of repetitions
# required for the first and n2 for the second.
# The angle for n2-n1 repetitions stisfies
# theta(n2-n1) != 0, -2pi/n <= theta(n2-n1) <= 2pi/n
# We therefore have he ability to do rotations around small angles.
# We can use this to rotate around angles that are as small as we
# like, just by increasing the number of times we repeat this gate.
# By using small-angle rotations, we can also rotate by any angle
# we like. This won't always be exact, but it is guaranteed to be 
# accurate up to 2pi/n, which we can be made as small as we like.
# Using the RzRx rotations allows us to have arbitrary rotaions
# around the Bloch Sphere, through the use of numerous T-Gate operations.
# Therefore the complexity of algorithms in quantum computations is
# often time given in terms of how many T-Gates they require.
qc = QuantumCircuit(1)
# Rx gate rotation about the x-axis:
qc.h(0)
qc.t(0)
qc.h(0)
# Rz gate rotation about the z-axis:
qc.t(0)
qc.draw()