Skip to content

Commit

Permalink
change dim dict to list, change noise config (#2)
Browse files Browse the repository at this point in the history
  • Loading branch information
nkanazawa1989 authored and nonhermitian committed Jun 24, 2019
1 parent 208ddd7 commit 494dcf2
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 85 deletions.
83 changes: 39 additions & 44 deletions qiskit/providers/aer/openpulse/qobj/operators.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ def gen_oper(opname, index, h_osc, h_qub, states=None):
----------
opname (str): Name of the operator to be returned.
index (int): Index of operator.
h_osc (dict): Dimension of oscillator subspace
h_qub (dict): Dimension of qubit subspace
h_osc (list): Dimension of oscillator subspace
h_qub (list): Dimension of qubit subspace
states (tuple): State indices of projection operator.
Returns
Expand All @@ -35,34 +35,32 @@ def gen_oper(opname, index, h_osc, h_qub, states=None):
# get number of levels in Hilbert space
if opname in ['X', 'Y', 'Z', 'Sp', 'Sm', 'I', 'O', 'P']:
is_qubit = True
dim = h_qub.get(index, 2)
dim = h_qub[index]
else:
is_qubit = False
dim = h_osc.get(index, 5)
dim = h_osc[index]

if opname == 'P':
opr_tmp = op.get_oper(opname, dim, states)
else:
opr_tmp = op.get_oper(opname, dim)

# reverse sort by index
rev_h_osc = sorted(h_osc.items(), key=lambda x: x[0])[::-1]
rev_h_qub = sorted(h_qub.items(), key=lambda x: x[0])[::-1]

# osc_n * … * osc_0 * qubit_n * … * qubit_0
# qubit_0 * … * qubit_n * osc_0 * … * osc_n
opers = []
for ii, dd in rev_h_osc:
if ii == index and not is_qubit:
for ii, dd in enumerate(h_qub):
if ii == index and is_qubit:
opers.append(opr_tmp)
else:
opers.append(op.qeye(dd))
for ii, dd in rev_h_qub:
if ii == index and is_qubit:
for ii, dd in enumerate(h_osc):
if ii == index and not is_qubit:
opers.append(opr_tmp)
else:
opers.append(op.qeye(dd))

return op.tensor(opers)
# return in reverse order
# osc_n * … * osc_0 * qubit_n * … * qubit_0
return op.tensor(opers[::-1])


def qubit_occ_oper(target_qubit, h_osc, h_qub, level=0):
Expand All @@ -73,29 +71,28 @@ def qubit_occ_oper(target_qubit, h_osc, h_qub, level=0):
Parameters
----------
target_qubit (int): Qubit for which operator is built.
h_osc (dict): Dict of number of levels in each oscillator.
h_qub (dict): Dict of number of levels in each qubit system.
h_osc (list): Dimension of oscillator subspace
h_qub (list): Dimension of qubit subspace
level (int): Level of qubit system to be measured.
Returns
-------
out_oper (qutip.Qobj): Occupation number operator for target qubit.
"""
# reverse sort by index
rev_h_osc = sorted(h_osc.items(), key=lambda x: x[0])[::-1]
rev_h_qub = sorted(h_qub.items(), key=lambda x: x[0])[::-1]

# osc_n * … * osc_0 * qubit_n * … * qubit_0
# qubit_0 * … * qubit_n * osc_0 * … * osc_n
opers = []
for ii, dd in rev_h_osc:
opers.append(op.qeye(dd))
for ii, dd in rev_h_qub:
for ii, dd in enumerate(h_qub):
if ii == target_qubit:
opers.append(op.fock_dm(h_qub.get(target_qubit, 2), level))
opers.append(op.fock_dm(dd, level))
else:
opers.append(op.qeye(dd))
for ii, dd in enumerate(h_osc):
opers.append(op.qeye(dd))

return op.tensor(opers)
# return in reverse order
# osc_n * … * osc_0 * qubit_n * … * qubit_0
return op.tensor(opers[::-1])


def measure_outcomes(measured_qubits, state_vector, measure_ops,
Expand Down Expand Up @@ -139,30 +136,29 @@ def apply_projector(measured_qubits, results, h_qub, h_osc, state_vector):
----------
measured_qubits (list): measured qubit indices.
results (list): results of qubit measurements.
h_qub (dict): Dict of number of levels in each qubit system.
h_osc (dict): Dict of number of levels in each oscillator.
h_osc (list): Dimension of oscillator subspace
h_qub (list): Dimension of qubit subspace
state_vector (ndarray): State vector.
Returns:
----------
proj_state (qutip.Qobj): State vector after projector applied, and normalized.
"""

# reverse sort by index
rev_h_osc = sorted(h_osc.items(), key=lambda x: x[0])[::-1]
rev_h_qub = sorted(h_qub.items(), key=lambda x: x[0])[::-1]

# osc_n * … * osc_0 * qubit_n * … * qubit_0
# qubit_0 * … * qubit_n * osc_0 * … * osc_n
opers = []
for ii, dd in rev_h_osc:
opers.append(op.qeye(dd))
for ii, dd in rev_h_qub:
for ii, dd in enumerate(h_qub):
if ii in measured_qubits:
opers.append(op.fock_dm(dd, results[ii]))
else:
opers.append(op.qeye(dd))
for ii, dd in enumerate(h_osc):
opers.append(op.qeye(dd))

# return in reverse order
# osc_n * … * osc_0 * qubit_n * … * qubit_0
proj_oper = op.tensor(opers[::-1])

proj_oper = op.tensor(opers)
psi = op.opr_apply(proj_oper, state_vector)
psi /= la.norm(psi)

Expand All @@ -181,13 +177,12 @@ def init_fock_state(h_osc, h_qub, noise_dict={}):
Returns:
qutip.Qobj: State vector
"""
# reverse sort by index
rev_h_osc = sorted(h_osc.items(), key=lambda x: x[0])[::-1]
rev_h_qub = sorted(h_qub.items(), key=lambda x: x[0])[::-1]

# osc_n * … * osc_0 * qubit_n * … * qubit_0
# qubit_0 * … * qubit_n * osc_0 * … * osc_n
sub_state_vecs = []
for ii, dd in rev_h_osc:
for ii, dd in enumerate(h_qub):
sub_state_vecs.append(op.basis(dd, 0))
for ii, dd in enumerate(h_osc):
n_thermal = noise_dict['oscillator']['n_th'].get(str(ii), 0)
if n_thermal == 0:
# no thermal particles
Expand All @@ -201,7 +196,7 @@ def init_fock_state(h_osc, h_qub, noise_dict={}):
cum_sum = np.cumsum(diags)
idx = np.where(np.random.random() < cum_sum)[0][0]
sub_state_vecs.append(op.basis(dd, idx))
for ii, dd in rev_h_qub:
sub_state_vecs.append(op.basis(dd, 0))

return op.tensor(sub_state_vecs)
# return in reverse order
# osc_n * … * osc_0 * qubit_n * … * qubit_0
return op.tensor(sub_state_vecs[::-1])
76 changes: 35 additions & 41 deletions qiskit/providers/aer/openpulse/qobj/opparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,15 @@
class HamiltonianParser:
""" Generate QuTip hamiltonian object from string
"""
def __init__(self, h_str, dim_osc, dim_qub):
def __init__(self, hamiltonian, dim_osc, dim_qub):
""" Create new quantum operator generator
Parameters:
h_str (list): list of Hamiltonian string
dim_osc (dict): dimension of oscillator subspace
dim_qub (dict): dimension of qubit subspace
hamiltonian (list): list of Hamiltonian string
dim_osc (list): dimension of oscillator subspace
dim_qub (list): dimension of qubit subspace
"""
self.h_str = h_str
self.hamiltonian = hamiltonian
self.dim_osc = dim_osc
self.dim_qub = dim_qub
self.__td_hams = []
Expand All @@ -72,7 +72,7 @@ def parse(self):
self._expand_sum()

# convert to reverse Polish notation
for ham in self.h_str:
for ham in self.hamiltonian:
if len(re.findall(r"\|\|", ham)) > 1:
raise Exception("Multiple time-dependent terms in %s" % ham)
p_td = re.search(r"(?P<opr>[\S]+)\|\|(?P<ch>[\S]+)", ham)
Expand Down Expand Up @@ -102,7 +102,7 @@ def _expand_sum(self):
sum_str = re.compile(r"_SUM\[(?P<itr>[a-z]),(?P<l>[a-z\d{}+-]+),(?P<u>[a-z\d{}+-]+),")
brk_str = re.compile(r"]")

ham_list = copy.copy(self.h_str)
ham_list = copy.copy(self.hamiltonian)
ham_out = []

while any(ham_list):
Expand Down Expand Up @@ -144,7 +144,7 @@ def _expand_sum(self):
trg_s, ham[p_brks[ii].end():]]))
ham_list.extend(_temp)

self.h_str = ham_out
self.hamiltonian = ham_out

return ham_out

Expand Down Expand Up @@ -279,33 +279,30 @@ def _token2qobj(self, tokens):

class NoiseParser:
""" Generate QuTip noise object from dictionary
Qubit noise is given in the format of nested dictionary:
"qubit": {
"0": {
"Sm": 0.006
}
}
and oscillator noise is given in the format of nested dictionary:
"oscillator": {
"n_th": {
"0": 0.001
},
"coupling": {
"0": 0.05
}
}
Qubit noise is given in the format of list of dictionary:
"qubit": [
{"Sm": 0.006}
]
and oscillator noise is given in the nested list of (n_th, coupling):
"oscillator": [
[0.001, 0.05]
]
these configurations are combined in the same dictionary
"""
def __init__(self, noise_dict, dim_osc, dim_qub):
""" Create new quantum operator generator
Parameters:
noise_dict (dict): dictionary of noise configuration
dim_osc (dict): dimension of oscillator subspace
dim_qub (dict): dimension of qubit subspace
dim_osc (list): dimension of oscillator subspace
dim_qub (list): dimension of qubit subspace
"""
self.noise_osc = noise_dict.get('oscillator', {'n_th': {}, 'coupling': {}})
self.noise_qub = noise_dict.get('qubit', {})
self.noise_osc = noise_dict.get('oscillator', [])
self.noise_qub = noise_dict.get('qubit', [])
self.dim_osc = dim_osc
self.dim_qub = dim_qub
self.__c_list = []
Expand All @@ -320,7 +317,7 @@ def parse(self):
""" Parse and generate quantum class object
"""
# Qubit noise
for index, config in self.noise_qub.items():
for index, config in enumerate(self.noise_qub):
for opname, coef in config.items():
# TODO: support noise in multi-dimensional system
# TODO: support noise with math operation
Expand All @@ -330,19 +327,16 @@ def parse(self):
raise Exception('Unsupported noise operator %s is given' % opname)
self.__c_list.append(np.sqrt(coef) * opr)
# Oscillator noise
ndic = self.noise_osc['n_th']
cdic = self.noise_osc['coupling']
for (n_ii, n_coef), (c_ii, c_coef) in zip(ndic.items(), cdic.items()):
if n_ii == c_ii:
if c_coef > 0:
opr = gen_oper('A', int(n_ii), self.dim_osc, self.dim_qub)
if n_coef:
self.__c_list.append(np.sqrt(c_coef * (1 + n_coef)) * opr)
self.__c_list.append(np.sqrt(c_coef * n_coef) * opr.dag())
else:
self.__c_list.append(np.sqrt(c_coef) * opr)
else:
raise Exception('Invalid oscillator index in noise dictionary.')
ndic = [nconf[0] for nconf in self.noise_osc]
cdic = [nconf[1] for nconf in self.noise_osc]
for ii, (n_coef, c_coef) in enumerate(zip(ndic, cdic)):
if c_coef > 0:
opr = gen_oper('A', int(ii), self.dim_osc, self.dim_qub)
if n_coef:
self.__c_list.append(np.sqrt(c_coef * (1 + n_coef)) * opr)
self.__c_list.append(np.sqrt(c_coef * n_coef) * opr.dag())
else:
self.__c_list.append(np.sqrt(c_coef) * opr)


def math_priority(o1, o2):
Expand Down

0 comments on commit 494dcf2

Please sign in to comment.