Getting the following exception when trying to get Hamiltonian for test data

Hi ,
I am getting following error when trying to get the Hamiltonian, Can you suggest what could be the reason causing the error ?I can share the snippet of code if you request .

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[4], line 20
     17 # Print the observables
     18 print(observables)
---> 20 hamiltonian = qml.Hamiltonian(test_cases, observables)

File D:\anaconda3\envs\QCML\Lib\site-packages\pennylane\ops\op_math\linear_combination.py:125, in LinearCombination.__init__(self, coeffs, observables, simplify, grouping_type, method, _grouping_indices, _pauli_rep, id)
    121     raise ValueError(
    122         "observables must be an Iterable of Operator's, and not an Operator itself."
    123     )
    124 if qml.math.shape(coeffs)[0] != len(observables):
--> 125     raise ValueError(
    126         "Could not create valid LinearCombination; "
    127         "number of coefficients and operators does not match."
    128     )
    129 if _pauli_rep is None:
    130     _pauli_rep = self._build_pauli_rep_static(coeffs, observables)

ValueError: Could not create valid LinearCombination; number of coefficients and operators does not match.

I was able to correct that error but now I get the following ( seems I need to convert to square matrix )

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[7], line 1
----> 1 cost = [optimize_circuit(reshaped_array,hamiltonian)]

File D:\anaconda3\envs\QCML\Lib\site-packages\pennylane\workflow\qnode.py:1092, in QNode.__call__(self, *args, **kwargs)
   1089     override_shots = kwargs["shots"]
   1091 # construct the tape
-> 1092 self.construct(args, kwargs)
   1094 original_grad_fn = [self.gradient_fn, self.gradient_kwargs, self.device]
   1095 self._update_gradient_fn(shots=override_shots, tape=self._tape)

File D:\anaconda3\envs\QCML\Lib\site-packages\pennylane\workflow\qnode.py:929, in QNode.construct(self, args, kwargs)
    926     self.interface = qml.math.get_interface(*args, *list(kwargs.values()))
    928 with qml.queuing.AnnotatedQueue() as q:
--> 929     self._qfunc_output = self.func(*args, **kwargs)
    931 self._tape = QuantumScript.from_queue(q, shots)
    933 params = self.tape.get_parameters(trainable_only=False)

Cell In[1], line 71, in optimize_circuit(params, hamiltonian)
     69 # Optimization loop
     70 for n in range(steps):
---> 71     theta, prev_cost = opt.step_and_cost(variational_circuit, params,hamiltonian)
     72     cost.append(cost_function(theta))
     73     angle.append(theta)    

File D:\anaconda3\envs\QCML\Lib\site-packages\pennylane\optimize\gradient_descent.py:64, in GradientDescentOptimizer.step_and_cost(self, objective_fn, grad_fn, *args, **kwargs)
     44 def step_and_cost(self, objective_fn, *args, grad_fn=None, **kwargs):
     45     """Update trainable arguments with one step of the optimizer and return the corresponding
     46     objective function value prior to the step.
     47 
   (...)
     61         If single arg is provided, list [array] is replaced by array.
     62     """
---> 64     g, forward = self.compute_grad(objective_fn, args, kwargs, grad_fn=grad_fn)
     65     new_args = self.apply_grad(g, args)
     67     if forward is None:

File D:\anaconda3\envs\QCML\Lib\site-packages\pennylane\optimize\gradient_descent.py:122, in GradientDescentOptimizer.compute_grad(objective_fn, args, kwargs, grad_fn)
    104 r"""Compute gradient of the objective function at the given point and return it along with
    105 the objective function forward pass (if available).
    106 
   (...)
    119     will not be evaluted and instead ``None`` will be returned.
    120 """
    121 g = get_gradient(objective_fn) if grad_fn is None else grad_fn
--> 122 grad = g(*args, **kwargs)
    123 forward = getattr(g, "forward", None)
    125 num_trainable_args = sum(getattr(arg, "requires_grad", False) for arg in args)

File D:\anaconda3\envs\QCML\Lib\site-packages\pennylane\_grad.py:165, in grad.__call__(self, *args, **kwargs)
    162     self._forward = self._fun(*args, **kwargs)
    163     return ()
--> 165 grad_value, ans = grad_fn(*args, **kwargs)  # pylint: disable=not-callable
    166 self._forward = ans
    168 return grad_value

File D:\anaconda3\envs\QCML\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 D:\anaconda3\envs\QCML\Lib\site-packages\pennylane\_grad.py:183, in grad._grad_with_forward(fun, x)
    177 @staticmethod
    178 @unary_to_nary
    179 def _grad_with_forward(fun, x):
    180     """This function is a replica of ``autograd.grad``, with the only
    181     difference being that it returns both the gradient *and* the forward pass
    182     value."""
--> 183     vjp, ans = _make_vjp(fun, x)  # pylint: disable=redefined-outer-name
    185     if vspace(ans).size != 1:
    186         raise TypeError(
    187             "Grad only applies to real scalar-output functions. "
    188             "Try jacobian, elementwise_grad or holomorphic_grad."
    189         )

File D:\anaconda3\envs\QCML\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 D:\anaconda3\envs\QCML\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 D:\anaconda3\envs\QCML\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)

File D:\anaconda3\envs\QCML\Lib\site-packages\pennylane\workflow\qnode.py:1092, in QNode.__call__(self, *args, **kwargs)
   1089     override_shots = kwargs["shots"]
   1091 # construct the tape
-> 1092 self.construct(args, kwargs)
   1094 original_grad_fn = [self.gradient_fn, self.gradient_kwargs, self.device]
   1095 self._update_gradient_fn(shots=override_shots, tape=self._tape)

File D:\anaconda3\envs\QCML\Lib\site-packages\pennylane\workflow\qnode.py:929, in QNode.construct(self, args, kwargs)
    926     self.interface = qml.math.get_interface(*args, *list(kwargs.values()))
    928 with qml.queuing.AnnotatedQueue() as q:
--> 929     self._qfunc_output = self.func(*args, **kwargs)
    931 self._tape = QuantumScript.from_queue(q, shots)
    933 params = self.tape.get_parameters(trainable_only=False)

Cell In[1], line 38, in variational_circuit(params, hamiltonian)
     36 parameters = params.reshape((LAYERS, WIRES, 3))
     37 qml.templates.StronglyEntanglingLayers(parameters, wires=range(WIRES))
---> 38 return qml.expval(qml.Hermitian(hamiltonian, wires = [0,1]))

File D:\anaconda3\envs\QCML\Lib\site-packages\pennylane\ops\qubit\observables.py:84, in Hermitian.__init__(self, A, wires, id)
     80     else:
     81         # Assumably wires is an int; further validation checks are performed by calling super().__init__
     82         expected_mx_shape = self._num_basis_states
---> 84     Hermitian._validate_input(A, expected_mx_shape)
     86 super().__init__(A, wires=wires, id=id)

File D:\anaconda3\envs\QCML\Lib\site-packages\pennylane\ops\qubit\observables.py:91, in Hermitian._validate_input(A, expected_mx_shape)
     88 @staticmethod
     89 def _validate_input(A, expected_mx_shape=None):
     90     """Validate the input matrix."""
---> 91     if len(A.shape) != 2 or A.shape[0] != A.shape[1]:
     92         raise ValueError("Observable must be a square matrix.")
     94     if expected_mx_shape is not None and A.shape[0] != expected_mx_shape:

AttributeError: 'LinearCombination' object has no attribute 'shape'

Hi @Rajesh_Chaurasia , welcome to the Forum!

Could you please provide the following information? It can help us understand the problem and find possible solutions:

  1. The output of qml.about()

  2. A minimal (but self-contained) working example
    This is the simplest version of the code that reproduces the problem. It should include all necessary imports, data, functions, etc., so that we can copy-paste the code and reproduce the problem. However it shouldn’t contain any unnecessary data, functions, …, for example gates and functions that can be removed to simplify the code.

If you’re not sure what these mean then make sure to check out this video :smiley:

Here is my reply as per above ask-

  1. **O/P of qml.about() is : **
    Name: PennyLane
    Version: 0.36.0
    Summary: PennyLane is a cross-platform Python library for quantum computing, quantum machine learning, and quantum chemistry. Train a quantum computer the same way as a neural network.
    Home-page: GitHub - PennyLaneAI/pennylane: PennyLane is a cross-platform Python library for quantum computing, quantum machine learning, and quantum chemistry. Train a quantum computer the same way as a neural network.
    Author:
    Author-email:
    License: Apache License 2.0
    Location: D:\anaconda3\envs\QCML\Lib\site-packages
    Requires: appdirs, autograd, autoray, cachetools, networkx, numpy, pennylane-lightning, requests, rustworkx, scipy, semantic-version, toml, typing-extensions
    Required-by: PennyLane_Lightning

Platform info: Windows-10-10.0.22631-SP0
Python version: 3.11.8
Numpy version: 1.23.4
Scipy version: 1.12.0
Installed devices:

  • default.clifford (PennyLane-0.36.0)
  • default.gaussian (PennyLane-0.36.0)
  • default.mixed (PennyLane-0.36.0)
  • default.qubit (PennyLane-0.36.0)
  • default.qubit.autograd (PennyLane-0.36.0)
  • default.qubit.jax (PennyLane-0.36.0)
  • default.qubit.legacy (PennyLane-0.36.0)
  • default.qubit.tf (PennyLane-0.36.0)
  • default.qubit.torch (PennyLane-0.36.0)
  • default.qutrit (PennyLane-0.36.0)
  • default.qutrit.mixed (PennyLane-0.36.0)
  • null.qubit (PennyLane-0.36.0)
  • lightning.qubit (PennyLane_Lightning-0.36.0)
  1. A minimal (but self-contained) working example
import json
import pennylane as qml
import pennylane.numpy as np
WIRES = 2
LAYERS = 5
NUM_PARAMETERS = LAYERS * WIRES * 3

initial_params = np.random.random(NUM_PARAMETERS)
######## ADDED START #########
dev = qml.device("default.qubit", WIRES)
######## ADDED END #########

@qml.qnode(dev)
def variational_circuit(params,hamiltonian):
    """
    This is a template variational quantum circuit containing a fixed layout of gates with variable
    parameters. To be used as a QNode, it must either be wrapped with the @qml.qnode decorator or
    converted using the qml.QNode function.

    The output of this circuit is the expectation value of a Hamiltonian, somehow encoded in
    the hamiltonian argument

    Args:
        - params (np.ndarray): An array of optimizable parameters of shape (30,)
        - hamiltonian (np.ndarray): An array of real parameters encoding the Hamiltonian
        whose expectation value is returned.

    Returns:
        (float): The expectation value of the Hamiltonian
    """
    parameters = params.reshape((LAYERS, WIRES, 3))
    qml.templates.StronglyEntanglingLayers(parameters, wires=range(WIRES))
    return qml.expval(qml.Hermitian(hamiltonian, wires = [0,1]))

@qml.qnode(dev)
def optimize_circuit(params,hamiltonian):
    """Minimize the variational circuit and return its minimum value.
    You should create a device and convert the variational_circuit function
    into an executable QNode.
    Next, you should minimize the variational circuit using gradient-based
    optimization to update the input params.
    Return the optimized value of the QNode as a single floating-point number.

    Args:
        - params (np.ndarray): Input parameters to be optimized, of dimension 30
        - hamiltonian (np.ndarray): An array of real parameters encoding the Hamiltonian
        whose expectation value you should minimize.
    Returns:
        float: the value of the optimized QNode
    """
    ######## ADDED START #########
    theta = np.array([0.0, 0.0,0.0], requires_grad=True) # Initial guess parameters
    angle = [theta] # Store the values of the circuit parameter
    cost = [cost_function(theta)] # Store the values of the cost function

    opt = qml.GradientDescentOptimizer() # optimizer chosen as per ask
    conv_tol = 1e-06 # Convergence threshold to stop optimization procedure

    # Number of optimization steps
    steps = 100
    exp_value_cost = float(0.0)
    # Optimization loop
    for n in range(steps):
        theta, prev_cost = opt.step_and_cost(variational_circuit, params,hamiltonian)
        cost.append(cost_function(theta))
        angle.append(theta)
        conv = np.abs(cost[-1] - prev_cost)
        if n % 10 == 0:
            print(f"Step = {n}")
        if (conv <= conv_tol).any():
            exp_value_cost = conv
            break
        #print(f"Step: {n}  Cost: {cost}")

    #print("\n" f"Final value of the cost function = {cost[-1]:.8f} ")
    #print("\n" f"Optimal value of the first circuit parameter =    "  + str(angle[-1][0]))
    #print("\n" f"Optimal value of the second circuit parameter =    "  + str(angle[-1][1]))

    return exp_value_cost
    ######## ADDED END #########

######## ADDED START #########
###$$$$$####
# Define Hamiltonian 
def circuit(params, wires=0):
    qml.RX(params[0], wires=wires)
    qml.RY(params[0], wires=wires)

coeffs = [1, 1]
obs = [qml.PauliX(0), qml.PauliZ(0)]

H = qml.Hamiltonian(coeffs, obs)

@qml.qnode(dev)
def cost_function(params):
    circuit(params)
    return qml.expval(H)

Hi @Rajesh_Chaurasia ,

Thanks for sharing this additional info.
I see several things happening here:

A. Install issues
You’re not using the latest PennyLane version and you seem to have installed PennyLane-Lightning separately, which isn’t necessary (and will cause issues in this case).
I recommended that you create a new virtual environment and follow the instructions below. You can create a virtual environment with Conda and install PennyLane as follows:

  1. Install Miniconda following the instructions here.
  2. Open your terminal (mac) or command line (Windows).
  3. Create a new Conda environment with: conda create --name <name_of_your_environment> python=3.10
  4. Activate the environment with: conda activate <name_of_your_environment>
  5. Install PennyLane with: python -m pip install pennylane
  6. Install other useful packages (such as Matplotlib) with: python -m pip install matplotlib

Note that you will be installing 2 packages here: PennyLane, and Matplotlib. Also, note that where it says <name_of_your_environment> you can choose any name that you want.

You don’t need to install lightning.qubit in addition to this, it already comes together with PennyLane.

B. You’re trying to turn things into a QNode that shouldn’t be QNodes
optimize_circuit() is a function that contains a ton of classical processing. QNodes are designed to attach a quantum circuit to a specific device, so it doesn’t work in this case.

C. There’s some confusion in your code
You have a bunch of disconnected functions and are trying to optimize the variational circuit directly instead of the cost function.

Next steps
I know this is a lot of information so here are some next steps I suggest:

  • Start by following my instructions from part A. You can also install Jupyter if you want to work with notebooks.
  • Watch this video on creating quantum circuits and QNodes
  • Watch this video on optimizing quantum circuits

I would recommend that you continue by running simple optimization examples (like this one) before going to more complex workflows.

I hope this helps!