Hello! I constructed a class that allows me to create a quantum system with a Hamiltonian that has two parts: a time-independent part and time-dependent part:
class QNN_layer():
def __init__(self, n_qubits, n_basis, timespan = 1, basis='fourier', pulse_width = 0.5e-2):
self.n_qubits = n_qubits
self.n_basis = n_basis
self.timespan = timespan
self.basis = basis
self.pulse_width = pulse_width
self.create_parameterized_hamiltonian()
def create_parameterized_hamiltonian(self):
self.create_all_to_all_hamiltonian()
self.create_control_hamiltonian()
self.H = self.H_all_to_all + self.H_control
def create_all_to_all_hamiltonian(self):
# Defining the collective S_z operator
# self.all_to_all_fields = [self.get_constant_field() for i in range(self.n_qubits)]
S_z = (1/2)*sum([qml.PauliZ(i) for i in range(self.n_qubits)])
self.S_z_squared = S_z @ S_z
# self.H_all_to_all = qml.dot([self.get_constant_field()], [self.S_z_squared])
self.H_all_to_all = qml.dot([1.0], [self.S_z_squared])
def create_control_hamiltonian(self):
MORE HERE BUT NOT RELEVANT
I’m trying to create a function that evolves the time-independent part in some interval ts=[0.0, 1.0] and calculates a value:
def get_loss_function_no_control(qnn_layer, ts, S_x, S_z, dev):
@jax.jit
@qml.qnode(dev, interface="jax")
def evol_no_control(param_vector):
# hamitlonian_params = dictionary_to_hamiltonian_parameters(params_dict)
param_list = qnn_layer.vector_to_hamiltonian_parameters(param_vector)
for i in range(qnn_layer.n_qubits): # Initializing in CSS
qml.Hadamard(wires=i)
qml.evolve(qnn_layer.H_all_to_all)(ts=ts) # Just doing the squeezing
return qml.var(S_z), qml.expval(S_x)
def loss_no_control(param_vector):
var, exp = evol_no_control(param_vector)
return qnn_layer.n_qubits * (var / (exp**2))
return loss_no_control
# Seed to make the results reproducible
seed = 0
# Setting up the problem
n_qubits = 5
n_basis = 6
timespan = 1.0
basis = 'gaussian'
pulse_width = 0.5e-2
ts = jnp.array([0.0, 1.0])
dev = qml.device("default.qubit.jax", wires = n_qubits)
qnn_layer = QNN_layer(n_qubits, n_basis, timespan=timespan, basis=basis, pulse_width=pulse_width)
S_x = (1/2)*sum([qml.PauliX(i) for i in range(qnn_layer.n_qubits)])
S_z = (1/2)*sum([qml.PauliZ(i) for i in range(qnn_layer.n_qubits)])
# Seed to make the results reproducible
seed = 0
loss_no_control = get_loss_function_no_control(qnn_layer, ts, S_x, S_z, dev)
param_vector = qnn_layer.get_random_parameter_vector(seed)
print(f'Initial loss: {loss_no_control(param_vector)}')
However, I get the error
{
"name": "TypeError",
"message": "'Evolution' object is not callable",
"stack": "---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[69], line 8
4 loss_no_control = get_loss_function_no_control(qnn_layer, ts, S_x, S_z, dev)
6 param_vector = qnn_layer.get_random_parameter_vector(seed)
----> 8 print(f'Initial loss: {loss_no_control(param_vector)}')
Cell In[68], line 19, in get_loss_function_no_control.<locals>.loss_no_control(param_vector)
18 def loss_no_control(param_vector):
---> 19 var, exp = evol_no_control(param_vector)
21 return qnn_layer.n_qubits * (var / (exp**2))
[... skipping hidden 11 frame]
File ~/.../pennylane/workflow/qnode.py:1164, in QNode.__call__(self, *args, **kwargs)
1162 if qml.capture.enabled():
1163 return qml.capture.qnode_call(self, *args, **kwargs)
-> 1164 return self._impl_call(*args, **kwargs)
File ~/.../pennylane/workflow/qnode.py:1144, in QNode._impl_call(self, *args, **kwargs)
1141 override_shots = kwargs[\"shots\"]
1143 # construct the tape
-> 1144 self.construct(args, kwargs)
1146 original_grad_fn = [self.gradient_fn, self.gradient_kwargs, self.device]
1147 self._update_gradient_fn(shots=override_shots, tape=self._tape)
File ~/.../pennylane/logging/decorators.py:61, in log_string_debug_func.<locals>.wrapper_entry(*args, **kwargs)
54 s_caller = \"::L\".join(
55 [str(i) for i in inspect.getouterframes(inspect.currentframe(), 2)[1][1:3]]
56 )
57 lgr.debug(
58 f\"Calling {f_string} from {s_caller}\",
59 **_debug_log_kwargs,
60 )
---> 61 return func(*args, **kwargs)
File ~/.../pennylane/workflow/qnode.py:966, in QNode.construct(self, args, kwargs)
964 with pldb_device_manager(self.device):
965 with qml.queuing.AnnotatedQueue() as q:
--> 966 self._qfunc_output = self.func(*args, **kwargs)
968 self._tape = QuantumScript.from_queue(q, shots)
970 params = self.tape.get_parameters(trainable_only=False)
Cell In[68], line 14, in get_loss_function_no_control.<locals>.evol_no_control(param_vector)
11 for i in range(qnn_layer.n_qubits): # Initializing in CSS
12 qml.Hadamard(wires=i)
---> 14 qml.evolve(qnn_layer.H_all_to_all)(ts=ts) # Just doing the squeezing
16 return qml.var(S_z), qml.expval(S_x)
TypeError: 'Evolution' object is not callable"
}
I see that here, instead of having a ParameterizedEvolution object as I was getting before with time-dependent control fields, now I’m just getting an Evolution object. How can I make it such that I calculate the value in loss_no_control that I need? Thank you!