2
$\begingroup$

I am working on a project to find a unitary gate that arrives at a specific density matrix at the end. The circuit construct is shown below:

dev1 = qml.device("default.mixed", wires=2)
@qml.qnode(dev1)
def circuit(params_SU):
    qml.QubitDensityMatrix(identity_matrix/2, 0)
    qml.QubitDensityMatrix(rho, 1)
    qml.SpecialUnitary(params_SU, [0, 1])
    return qml.density_matrix([0, 1])

And the cost function is:

expected_result = rho / 2 + identity_matrix / 4
def cost(params_SU):
    cost_value = np.linalg.norm(partial_trace_B(circuit(params_SU)) - expected_result)
    return cost_value

and

def partial_trace_B(matrix):

    partial_trace_B = np.zeros([2, 2], dtype=complex)

    partial_trace_B[0][0] = matrix[0][0] + matrix[1][1]
    partial_trace_B[0][1] = matrix[0][2] + matrix[1][3]
    partial_trace_B[1][0] = matrix[2][0] + matrix[3][1]
    partial_trace_B[1][1] = matrix[2][2] + matrix[3][3]

    return partial_trace_B

However, when running the optimisation:

n_steps = 1500
theta = np.random.rand(1, 15, requires_grad=True)
costs_list = []
opt = AdamOptimizer()

for i in range(1, n_steps+1):
    if i%100==0: print("Running... Current step: ", i)
    theta = opt.step(cost, theta)
    costs_list.append(cost(theta))

I kept getting this Value Error:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
TypeError: float() argument must be a string or a real number, not 'ArrayBox'

The above exception was the direct cause of the following exception:

ValueError                                Traceback (most recent call last)
Cell In[6], line 8
      6 for i in range(1, n_steps+1):
      7     if i%100==0: print("Running... Current step: ", i)
----> 8     theta = opt.step(cost, theta)
      9     costs_list.append(cost(theta))

File c:\Users\86986\AppData\Local\Programs\Python\Python311\Lib\site-packages\pennylane\optimize\gradient_descent.py:88, in GradientDescentOptimizer.step(self, objective_fn, grad_fn, *args, **kwargs)
     70 def step(self, objective_fn, *args, grad_fn=None, **kwargs):
     71     """Update trainable arguments with one step of the optimizer.
     72 
     73     Args:
   (...)
     85         If single arg is provided, list [array] is replaced by array.
     86     """
---> 88     g, _ = self.compute_grad(objective_fn, args, kwargs, grad_fn=grad_fn)
     89     new_args = self.apply_grad(g, args)
     91     # unwrap from list if one argument, cleaner return

File c:\Users\86986\AppData\Local\Programs\Python\Python311\Lib\site-packages\pennylane\optimize\gradient_descent.py:117, in GradientDescentOptimizer.compute_grad(objective_fn, args, kwargs, grad_fn)
     99 r"""Compute gradient of the objective function at the given point and return it along with
    100 the objective function forward pass (if available).
    101 
   (...)
    114     will not be evaluted and instead ``None`` will be returned.
    115 """
    116 g = get_gradient(objective_fn) if grad_fn is None else grad_fn
--> 117 grad = g(*args, **kwargs)
    118 forward = getattr(g, "forward", None)
    120 num_trainable_args = sum(getattr(arg, "requires_grad", False) for arg in args)

File c:\Users\86986\AppData\Local\Programs\Python\Python311\Lib\site-packages\pennylane\_grad.py:118, in grad.__call__(self, *args, **kwargs)
    115     self._forward = self._fun(*args, **kwargs)
    116     return ()
--> 118 grad_value, ans = grad_fn(*args, **kwargs)  # pylint: disable=not-callable
    119 self._forward = ans
    121 return grad_value

File c:\Users\86986\AppData\Local\Programs\Python\Python311\Lib\site-packages\autograd\wrap_util.py:20, in unary_to_nary.<locals>.nary_operator.<locals>.nary_f(*args, **kwargs)
     18 else:
     19     x = tuple(args[i] for i in argnum)
---> 20 return unary_operator(unary_f, x, *nary_op_args, **nary_op_kwargs)

File c:\Users\86986\AppData\Local\Programs\Python\Python311\Lib\site-packages\pennylane\_grad.py:136, in grad._grad_with_forward(fun, x)
    130 @staticmethod
    131 @unary_to_nary
    132 def _grad_with_forward(fun, x):
    133     """This function is a replica of ``autograd.grad``, with the only
    134     difference being that it returns both the gradient *and* the forward pass
    135     value."""
--> 136     vjp, ans = _make_vjp(fun, x)
    138     if not vspace(ans).size == 1:
    139         raise TypeError(
    140             "Grad only applies to real scalar-output functions. "
    141             "Try jacobian, elementwise_grad or holomorphic_grad."
    142         )

File c:\Users\86986\AppData\Local\Programs\Python\Python311\Lib\site-packages\autograd\core.py:10, in make_vjp(fun, x)
      8 def make_vjp(fun, x):
      9     start_node = VJPNode.new_root()
---> 10     end_value, end_node =  trace(start_node, fun, x)
     11     if end_node is None:
     12         def vjp(g): return vspace(x).zeros()

File c:\Users\86986\AppData\Local\Programs\Python\Python311\Lib\site-packages\autograd\tracer.py:10, in trace(start_node, fun, x)
      8 with trace_stack.new_trace() as t:
      9     start_box = new_box(x, t, start_node)
---> 10     end_box = fun(start_box)
     11     if isbox(end_box) and end_box._trace == start_box._trace:
     12         return end_box._value, end_box._node

File c:\Users\86986\AppData\Local\Programs\Python\Python311\Lib\site-packages\autograd\wrap_util.py:15, in unary_to_nary.<locals>.nary_operator.<locals>.nary_f.<locals>.unary_f(x)
     13 else:
     14     subargs = subvals(args, zip(argnum, x))
---> 15 return fun(*subargs, **kwargs)

Cell In[5], line 4
      3 def cost(params_SU):
----> 4     cost_value = np.linalg.norm(partial_trace_B(circuit(params_SU)) - expected_result)
      5     return cost_value

File c:\Users\86986\AppData\Local\Programs\Python\Python311\Lib\site-packages\pennylane\qnode.py:1027, in QNode.__call__(self, *args, **kwargs)
   1022         full_transform_program._set_all_argnums(
   1023             self, args, kwargs, argnums
   1024         )  # pylint: disable=protected-access
   1026 # pylint: disable=unexpected-keyword-arg
-> 1027 res = qml.execute(
   1028     (self._tape,),
   1029     device=self.device,
   1030     gradient_fn=self.gradient_fn,
   1031     interface=self.interface,
   1032     transform_program=full_transform_program,
   1033     config=config,
   1034     gradient_kwargs=self.gradient_kwargs,
   1035     override_shots=override_shots,
   1036     **self.execute_kwargs,
   1037 )
   1039 res = res[0]
   1041 # convert result to the interface in case the qfunc has no parameters

File c:\Users\86986\AppData\Local\Programs\Python\Python311\Lib\site-packages\pennylane\interfaces\execution.py:616, in execute(tapes, device, gradient_fn, interface, transform_program, config, grad_on_execution, gradient_kwargs, cache, cachesize, max_diff, override_shots, expand_fn, max_expansion, device_batch_transform)
    614 # Exiting early if we do not need to deal with an interface boundary
    615 if no_interface_boundary_required:
--> 616     results = inner_execute(tapes)
    617     return post_processing(results)
    619 _grad_on_execution = False

File c:\Users\86986\AppData\Local\Programs\Python\Python311\Lib\site-packages\pennylane\interfaces\execution.py:249, in _make_inner_execute.<locals>.inner_execute(tapes, **_)
    247 if numpy_only:
    248     tapes = tuple(qml.transforms.convert_to_numpy_parameters(t) for t in tapes)
--> 249 return cached_device_execution(tapes)

File c:\Users\86986\AppData\Local\Programs\Python\Python311\Lib\site-packages\pennylane\interfaces\execution.py:371, in cache_execute.<locals>.wrapper(tapes, **kwargs)
    366         return (res, []) if return_tuple else res
    368 else:
    369     # execute all unique tapes that do not exist in the cache
    370     # convert to list as new device interface returns a tuple
--> 371     res = list(fn(tuple(execution_tapes.values()), **kwargs))
    373 final_res = []
    375 for i, tape in enumerate(tapes):

File c:\Users\86986\AppData\Local\Programs\Python\Python311\Lib\contextlib.py:81, in ContextDecorator.__call__.<locals>.inner(*args, **kwds)
     78 @wraps(func)
     79 def inner(*args, **kwds):
     80     with self._recreate_cm():
---> 81         return func(*args, **kwds)

File c:\Users\86986\AppData\Local\Programs\Python\Python311\Lib\site-packages\pennylane\_qubit_device.py:460, in QubitDevice.batch_execute(self, circuits)
    455 for circuit in circuits:
    456     # we need to reset the device here, else it will
    457     # not start the next computation in the zero state
    458     self.reset()
--> 460     res = self.execute(circuit)
    461     results.append(res)
    463 if self.tracker.active:

File c:\Users\86986\AppData\Local\Programs\Python\Python311\Lib\site-packages\pennylane\devices\default_mixed.py:685, in DefaultMixed.execute(self, circuit, **kwargs)
    683         wires_list.append(m.wires)
    684     self.measured_wires = qml.wires.Wires.all_wires(wires_list)
--> 685 return super().execute(circuit, **kwargs)

File c:\Users\86986\AppData\Local\Programs\Python\Python311\Lib\site-packages\pennylane\_qubit_device.py:279, in QubitDevice.execute(self, circuit, **kwargs)
    276 self.check_validity(circuit.operations, circuit.observables)
    278 # apply all circuit operations
--> 279 self.apply(circuit.operations, rotations=self._get_diagonalizing_gates(circuit), **kwargs)
    281 # generate computational basis samples
    282 if self.shots is not None or circuit.is_sampled:

File c:\Users\86986\AppData\Local\Programs\Python\Python311\Lib\site-packages\pennylane\devices\default_mixed.py:699, in DefaultMixed.apply(self, operations, rotations, **kwargs)
    693         raise DeviceError(
    694             f"Operation {operation.name} cannot be used after other Operations have already been applied "
    695             f"on a {self.short_name} device."
    696         )
    698 for operation in operations:
--> 699     self._apply_operation(operation)
    701 # store the pre-rotated state
    702 self._pre_rotated_state = self._state

File c:\Users\86986\AppData\Local\Programs\Python\Python311\Lib\site-packages\pennylane\devices\default_mixed.py:617, in DefaultMixed._apply_operation(self, operation)
    614             self._debugger.snapshots[len(self._debugger.snapshots)] = density_matrix
    615     return
--> 617 matrices = self._get_kraus(operation)
    619 if operation in diagonal_in_z_basis:
    620     self._apply_diagonal_unitary(matrices, wires)

File c:\Users\86986\AppData\Local\Programs\Python\Python311\Lib\site-packages\pennylane\devices\default_mixed.py:308, in DefaultMixed._get_kraus(self, operation)
    305 if isinstance(operation, Channel):
    306     return operation.kraus_matrices()
--> 308 return [operation.matrix()]

File c:\Users\86986\AppData\Local\Programs\Python\Python311\Lib\site-packages\pennylane\operation.py:775, in Operator.matrix(self, wire_order)
    755 def matrix(self, wire_order=None):
    756     r"""Representation of the operator as a matrix in the computational basis.
    757 
    758     If ``wire_order`` is provided, the numerical representation considers the position of the
   (...)
    773         tensor_like: matrix representation
    774     """
--> 775     canonical_matrix = self.compute_matrix(*self.parameters, **self.hyperparameters)
    777     if wire_order is None or self.wires == Wires(wire_order):
    778         return canonical_matrix

File c:\Users\86986\AppData\Local\Programs\Python\Python311\Lib\site-packages\pennylane\ops\qubit\special_unitary.py:482, in SpecialUnitary.compute_matrix(theta, num_wires)
    479 if interface == "jax" and qml.math.ndim(theta) > 1:
    480     # jax.numpy.expm does not support broadcasting
    481     return qml.math.stack([qml.math.expm(1j * _A) for _A in A])
--> 482 return qml.math.expm(1j * A)

File c:\Users\86986\AppData\Local\Programs\Python\Python311\Lib\site-packages\pennylane\math\multi_dispatch.py:151, in multi_dispatch.<locals>.decorator.<locals>.wrapper(*args, **kwargs)
    148 interface = interface or get_interface(*dispatch_args)
    149 kwargs["like"] = interface
--> 151 return fn(*args, **kwargs)

File c:\Users\86986\AppData\Local\Programs\Python\Python311\Lib\site-packages\pennylane\math\multi_dispatch.py:837, in expm(tensor, like)
    834     return tf.linalg.expm(tensor)
    835 from scipy.linalg import expm as scipy_expm
--> 837 return scipy_expm(tensor)

File c:\Users\86986\AppData\Local\Programs\Python\Python311\Lib\site-packages\scipy\linalg\_matfuncs.py:299, in expm(A)
    296     return np.exp(a)
    298 if not np.issubdtype(a.dtype, np.inexact):
--> 299     a = a.astype(float)
    300 elif a.dtype == np.float16:
    301     a = a.astype(np.float32)

ValueError: setting an array element with a sequence.

It would be very kind if someone can provide some resolutions. Thank you so much!!! I believe the bug is due to the line in the cost function

cost_value = np.linalg.norm(partial_trace_B(circuit(params_SU)) - expected_result)

But it seems completely fine with me, and I do not know how to solve it.

$\endgroup$
3
  • $\begingroup$ just curious, what is your import statement for numpy (which defines the alias np)? $\endgroup$
    – co9olguy
    Commented Mar 19 at 15:16
  • $\begingroup$ @co9olguy Thank you for the comment. It is from pennylane import numpy as np. $\endgroup$
    – JiQing
    Commented Mar 19 at 15:26
  • 1
    $\begingroup$ And where is rho defined? It's hard to help if you don't provide an example with all the ingredients available $\endgroup$
    – co9olguy
    Commented Mar 19 at 15:55

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.