Skip to content

Commit

Permalink
Factor out oper parsing, add qopt.DenseOperator detection
Browse files Browse the repository at this point in the history
Used in both Basis and PulseSequence constructors
  • Loading branch information
thangleiter committed Mar 2, 2021
1 parent 35efbf5 commit a4d39eb
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 33 deletions.
12 changes: 1 addition & 11 deletions filter_functions/basis.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,21 +173,11 @@ def __new__(cls, basis_array: Sequence, traceless: Optional[bool] = None,
except AttributeError:
pass

basis = np.empty((len(basis_array), *basis_array[0].shape), dtype=complex)
basis = util.parse_operators(basis_array, 'basis_array')
if basis.shape[0] > np.product(basis.shape[1:]):
raise ValueError('Given overcomplete set of basis matrices. '
'Not linearly independent.')

for i, elem in enumerate(basis_array):
if isinstance(elem, ndarray): # numpy array
basis[i] = elem
elif hasattr(elem, 'full'): # qutip.Qobj
basis[i] = elem.full()
elif hasattr(elem, 'todense'): # sparse array
basis[i] = elem.todense()
else:
raise TypeError('At least one element invalid type!')

basis = basis.view(cls)
basis.btype = btype or 'Custom'
basis.d = basis.shape[-1]
Expand Down
24 changes: 2 additions & 22 deletions filter_functions/pulse_sequence.py
Original file line number Diff line number Diff line change
Expand Up @@ -1134,28 +1134,8 @@ def _parse_Hamiltonian(H: Hamiltonian, n_dt: int, H_str: str) -> Tuple[Sequence[
coeffs = args[0]
identifiers = list(args[1])

# Parse opers and convert to squeezed ndarray if possible
parsed_opers = []
for oper in opers:
if isinstance(oper, ndarray):
parsed_opers.append(oper.squeeze())
elif hasattr(oper, 'full'):
# qutip.Qobj
parsed_opers.append(oper.full())
elif hasattr(oper, 'todense'):
# sparse object
parsed_opers.append(oper.todense())
else:
raise TypeError(f'Expected operators in {H_str} to be NumPy arrays or QuTiP Qobjs!')

# Check correct dimensions for the operators
if set(oper.ndim for oper in parsed_opers) != {2}:
raise ValueError(f'Expected all operators in {H_str} to be two-dimensional!')

if len(set(*set(oper.shape for oper in parsed_opers))) != 1:
raise ValueError(f'Expected operators in {H_str} to be square!')

parsed_opers = np.asarray(parsed_opers)
# Parse opers and convert to ndarray
parsed_opers = util.parse_operators(opers, H_str)

if not all(hasattr(coeff, '__len__') for coeff in coeffs):
raise TypeError(f'Expected coefficients in {H_str} to be a sequence')
Expand Down
49 changes: 49 additions & 0 deletions filter_functions/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,55 @@ def wrapper(*args, **kwargs):
parse_which_FF_parameter = parse_optional_parameters({'which': ('fidelity', 'generalized')})


def parse_operators(opers: Sequence[Operator], err_loc: str) -> List[ndarray]:
"""Parse a sequence of operators and convert to ndarray.
Parameters
----------
opers: Sequence[Operator]
Sequence of operators.
err_loc: str
Some cosmetics for the exceptions to be raised.
Raises
------
TypeError
If any operator is not a valid type.
ValueError
If not all operators are 2d and square.
Returns
-------
parse_opers: ndarray, shape (len(opers), *opers[0].shape)
The parsed ndarray.
"""
parsed_opers = []
for oper in opers:
if isinstance(oper, ndarray):
parsed_opers.append(oper.squeeze())
elif hasattr(oper, 'full'):
# qutip.Qobj
parsed_opers.append(oper.full())
elif hasattr(oper, 'todense'):
# sparse object
parsed_opers.append(oper.todense())
elif hasattr(oper, 'data') and hasattr(oper, 'dexp'):
# qopt DenseMatrix
parsed_opers.append(oper.data)
else:
raise TypeError(f'Expected operators in {err_loc} to be NumPy arrays or QuTiP Qobjs!')

# Check correct dimensions of the operators
if set(oper.ndim for oper in parsed_opers) != {2}:
raise ValueError(f'Expected all operators in {err_loc} to be two-dimensional!')

if len(set(*set(oper.shape for oper in parsed_opers))) != 1:
raise ValueError(f'Expected operators in {err_loc} to be square!')

return np.asarray(parsed_opers)


def _tensor_product_shape(shape_A: Sequence[int], shape_B: Sequence[int], rank: int):
"""Get shape of the tensor product between A and B of rank rank"""
broadcast_shape = ()
Expand Down

0 comments on commit a4d39eb

Please sign in to comment.