Hi,
I’m running into an error with using the mitiq module for error mitigation. Just running the forward pass (ie. cost_loc(problem, w, local_hadamard_test)
) seems to work perfectly fine, but when fed into the optimizer it results in the error below. The error looks the same as this post but I’ve tried @isaacdevlugt’s suggestions of explicitly specifying the kwargs + refactoring the QNode definition to be outside of the cost function, but I’m getting the same error.
Happy holidays!
Jeffrey
Error
{
"name": "CircuitConversionError",
"message": "Circuit could not be converted to an internal Mitiq circuit. This may be because the circuit contains custom gates or Pragmas (pyQuil). If you think this is a bug or that this circuit should be supported, you can open an issue at https://github.com/unitaryfund/mitiq.
Provided circuit has type <class 'pennylane.tape.qscript.QuantumScript'> and is:
<QuantumScript: wires=[3, 0, 1, 2], params=3>
Circuit types supported by Mitiq are
{'cirq': 'Circuit', 'pyquil': 'Program', 'qiskit': 'QuantumCircuit', 'braket': 'Circuit', 'pennylane': 'QuantumTape'}.",
"stack": "---------------------------------------------------------------------------
QasmException Traceback (most recent call last)
File ~/Downloads/Algorithm-Research/Student-Hub/Indy-Ng/.venv/lib/python3.11/site-packages/mitiq/interface/conversions.py:123, in convert_to_mitiq(circuit)
122 try:
--> 123 mitiq_circuit = conversion_function(circuit)
124 except Exception:
File ~/Downloads/Algorithm-Research/Student-Hub/Indy-Ng/.venv/lib/python3.11/site-packages/mitiq/interface/mitiq_pennylane/conversions.py:61, in from_pennylane(tape)
59 qasm = tape.to_openqasm(rotations=False, wires=wires, measure_all=False)
---> 61 return cirq_from_qasm(qasm)
File ~/Downloads/Algorithm-Research/Student-Hub/Indy-Ng/.venv/lib/python3.11/site-packages/mitiq/interface/mitiq_qiskit/conversions.py:299, in from_qasm(qasm)
298 qasm = _remove_qasm_barriers(qasm)
--> 299 return circuit_from_qasm(qasm)
File ~/Downloads/Algorithm-Research/Student-Hub/Indy-Ng/.venv/lib/python3.11/site-packages/cirq/contrib/qasm_import/qasm.py:29, in circuit_from_qasm(qasm)
20 \"\"\"Parses an OpenQASM string to `cirq.Circuit`.
21
22 Args:
(...)
26 The parsed circuit
27 \"\"\"
---> 29 return QasmParser().parse(qasm).circuit
File ~/Downloads/Algorithm-Research/Student-Hub/Indy-Ng/.venv/lib/python3.11/site-packages/cirq/contrib/qasm_import/_parser.py:538, in QasmParser.parse(self, qasm)
537 self.lexer.input(self.qasm)
--> 538 self.parsedQasm = self.parser.parse(lexer=self.lexer)
539 return self.parsedQasm
File ~/Downloads/Algorithm-Research/Student-Hub/Indy-Ng/.venv/lib/python3.11/site-packages/ply/yacc.py:333, in LRParser.parse(self, input, lexer, debug, tracking, tokenfunc)
332 else:
--> 333 return self.parseopt_notrack(input, lexer, debug, tracking, tokenfunc)
File ~/Downloads/Algorithm-Research/Student-Hub/Indy-Ng/.venv/lib/python3.11/site-packages/ply/yacc.py:1201, in LRParser.parseopt_notrack(self, input, lexer, debug, tracking, tokenfunc)
1200 self.state = state
-> 1201 tok = call_errorfunc(self.errorfunc, errtoken, self)
1202 if self.errorok:
1203 # User must have done some kind of panic
1204 # mode recovery on their own. The
1205 # returned token is the next lookahead
File ~/Downloads/Algorithm-Research/Student-Hub/Indy-Ng/.venv/lib/python3.11/site-packages/ply/yacc.py:192, in call_errorfunc(errorfunc, token, parser)
191 _restart = parser.restart
--> 192 r = errorfunc(token)
193 try:
File ~/Downloads/Algorithm-Research/Student-Hub/Indy-Ng/.venv/lib/python3.11/site-packages/cirq/contrib/qasm_import/_parser.py:521, in QasmParser.p_error(self, p)
519 raise QasmException('Unexpected end of file')
--> 521 raise QasmException(
522 f\"\"\"Syntax error: '{p.value}'
523 {self.debug_context(p)}
524 at line {p.lineno}, column {self.find_column(p)}\"\"\"
525 )
QasmException: Syntax error: 'ArrayBox'
...grad ArrayBox with value -1.8812601840080732) q[0];
ry(Autograd ArrayBox with value -0.29327952123155937) q[1];
ry(Autograd ArrayBox with value 0.3590991360570879) q[2];
h q[0];
h q[1];
cz q[3],q[0];
h q[0];
h q[1];
h q[3]
^
at line 6, column 13
During handling of the above exception, another exception occurred:
CircuitConversionError Traceback (most recent call last)
/Users/bigsad/Downloads/Algorithm-Research/Student-Hub/Indy-Ng/mitiq_integration.ipynb セル 1 line 2
<a href='vscode-notebook-cell:/Users/bigsad/Downloads/Algorithm-Research/Student-Hub/Indy-Ng/mitiq_integration.ipynb#W0sZmlsZQ%3D%3D?line=228'>229</a> start = time.time()
<a href='vscode-notebook-cell:/Users/bigsad/Downloads/Algorithm-Research/Student-Hub/Indy-Ng/mitiq_integration.ipynb#W0sZmlsZQ%3D%3D?line=230'>231</a> for it in range(10):
<a href='vscode-notebook-cell:/Users/bigsad/Downloads/Algorithm-Research/Student-Hub/Indy-Ng/mitiq_integration.ipynb#W0sZmlsZQ%3D%3D?line=231'>232</a> # w, cost = opt.step_and_cost(cost_agg, w)
--> <a href='vscode-notebook-cell:/Users/bigsad/Downloads/Algorithm-Research/Student-Hub/Indy-Ng/mitiq_integration.ipynb#W0sZmlsZQ%3D%3D?line=232'>233</a> w, cost = opt.step_and_cost(lambda w: cost_loc(problem, w, local_hadamard_test), w)
<a href='vscode-notebook-cell:/Users/bigsad/Downloads/Algorithm-Research/Student-Hub/Indy-Ng/mitiq_integration.ipynb#W0sZmlsZQ%3D%3D?line=234'>235</a> print(\"Step {:3d} Cost_L = {:9.7f}\".format(it, cost), flush=True)
<a href='vscode-notebook-cell:/Users/bigsad/Downloads/Algorithm-Research/Student-Hub/Indy-Ng/mitiq_integration.ipynb#W0sZmlsZQ%3D%3D?line=236'>237</a> it += 1
File ~/Downloads/Algorithm-Research/Student-Hub/Indy-Ng/.venv/lib/python3.11/site-packages/pennylane/optimize/gradient_descent.py:59, in GradientDescentOptimizer.step_and_cost(self, objective_fn, grad_fn, *args, **kwargs)
39 def step_and_cost(self, objective_fn, *args, grad_fn=None, **kwargs):
40 \"\"\"Update trainable arguments with one step of the optimizer and return the corresponding
41 objective function value prior to the step.
42
(...)
56 If single arg is provided, list [array] is replaced by array.
57 \"\"\"
---> 59 g, forward = self.compute_grad(objective_fn, args, kwargs, grad_fn=grad_fn)
60 new_args = self.apply_grad(g, args)
62 if forward is None:
File ~/Downloads/Algorithm-Research/Student-Hub/Indy-Ng/.venv/lib/python3.11/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 ~/Downloads/Algorithm-Research/Student-Hub/Indy-Ng/.venv/lib/python3.11/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 ~/Downloads/Algorithm-Research/Student-Hub/Indy-Ng/.venv/lib/python3.11/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 ~/Downloads/Algorithm-Research/Student-Hub/Indy-Ng/.venv/lib/python3.11/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 ~/Downloads/Algorithm-Research/Student-Hub/Indy-Ng/.venv/lib/python3.11/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 ~/Downloads/Algorithm-Research/Student-Hub/Indy-Ng/.venv/lib/python3.11/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 ~/Downloads/Algorithm-Research/Student-Hub/Indy-Ng/.venv/lib/python3.11/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)
/Users/bigsad/Downloads/Algorithm-Research/Student-Hub/Indy-Ng/mitiq_integration.ipynb セル 1 line 2
<a href='vscode-notebook-cell:/Users/bigsad/Downloads/Algorithm-Research/Student-Hub/Indy-Ng/mitiq_integration.ipynb#W0sZmlsZQ%3D%3D?line=228'>229</a> start = time.time()
<a href='vscode-notebook-cell:/Users/bigsad/Downloads/Algorithm-Research/Student-Hub/Indy-Ng/mitiq_integration.ipynb#W0sZmlsZQ%3D%3D?line=230'>231</a> for it in range(10):
<a href='vscode-notebook-cell:/Users/bigsad/Downloads/Algorithm-Research/Student-Hub/Indy-Ng/mitiq_integration.ipynb#W0sZmlsZQ%3D%3D?line=231'>232</a> # w, cost = opt.step_and_cost(cost_agg, w)
--> <a href='vscode-notebook-cell:/Users/bigsad/Downloads/Algorithm-Research/Student-Hub/Indy-Ng/mitiq_integration.ipynb#W0sZmlsZQ%3D%3D?line=232'>233</a> w, cost = opt.step_and_cost(lambda w: cost_loc(problem, w, local_hadamard_test), w)
<a href='vscode-notebook-cell:/Users/bigsad/Downloads/Algorithm-Research/Student-Hub/Indy-Ng/mitiq_integration.ipynb#W0sZmlsZQ%3D%3D?line=234'>235</a> print(\"Step {:3d} Cost_L = {:9.7f}\".format(it, cost), flush=True)
<a href='vscode-notebook-cell:/Users/bigsad/Downloads/Algorithm-Research/Student-Hub/Indy-Ng/mitiq_integration.ipynb#W0sZmlsZQ%3D%3D?line=236'>237</a> it += 1
/Users/bigsad/Downloads/Algorithm-Research/Student-Hub/Indy-Ng/mitiq_integration.ipynb セル 1 line 7
<a href='vscode-notebook-cell:/Users/bigsad/Downloads/Algorithm-Research/Student-Hub/Indy-Ng/mitiq_integration.ipynb#W0sZmlsZQ%3D%3D?line=71'>72</a> for lp in range(0, len(c)):
<a href='vscode-notebook-cell:/Users/bigsad/Downloads/Algorithm-Research/Student-Hub/Indy-Ng/mitiq_integration.ipynb#W0sZmlsZQ%3D%3D?line=72'>73</a> for j in range(0, n_qubits):
---> <a href='vscode-notebook-cell:/Users/bigsad/Downloads/Algorithm-Research/Student-Hub/Indy-Ng/mitiq_integration.ipynb#W0sZmlsZQ%3D%3D?line=73'>74</a> mu_sum = mu_sum + c[l] * np.conj(c[lp]) * mu(weights, local_hadamard_test, problem, l, lp, j)
<a href='vscode-notebook-cell:/Users/bigsad/Downloads/Algorithm-Research/Student-Hub/Indy-Ng/mitiq_integration.ipynb#W0sZmlsZQ%3D%3D?line=75'>76</a> mu_sum = abs(mu_sum)
<a href='vscode-notebook-cell:/Users/bigsad/Downloads/Algorithm-Research/Student-Hub/Indy-Ng/mitiq_integration.ipynb#W0sZmlsZQ%3D%3D?line=77'>78</a> # Cost function C_L
/Users/bigsad/Downloads/Algorithm-Research/Student-Hub/Indy-Ng/mitiq_integration.ipynb セル 1 line 4
<a href='vscode-notebook-cell:/Users/bigsad/Downloads/Algorithm-Research/Student-Hub/Indy-Ng/mitiq_integration.ipynb#W0sZmlsZQ%3D%3D?line=44'>45</a> def mu(weights, local_hadamard_test, problem, l=None, lp=None, j=None):
<a href='vscode-notebook-cell:/Users/bigsad/Downloads/Algorithm-Research/Student-Hub/Indy-Ng/mitiq_integration.ipynb#W0sZmlsZQ%3D%3D?line=45'>46</a> \"\"\"Generates the coefficients to compute the \"local\" cost function C_L.\"\"\"
---> <a href='vscode-notebook-cell:/Users/bigsad/Downloads/Algorithm-Research/Student-Hub/Indy-Ng/mitiq_integration.ipynb#W0sZmlsZQ%3D%3D?line=47'>48</a> mu_real = local_hadamard_test(weights, problem, l=l, lp=lp, j=j, part=\"Re\")
<a href='vscode-notebook-cell:/Users/bigsad/Downloads/Algorithm-Research/Student-Hub/Indy-Ng/mitiq_integration.ipynb#W0sZmlsZQ%3D%3D?line=48'>49</a> mu_imag = local_hadamard_test(weights, problem, l=l, lp=lp, j=j, part=\"Im\")
<a href='vscode-notebook-cell:/Users/bigsad/Downloads/Algorithm-Research/Student-Hub/Indy-Ng/mitiq_integration.ipynb#W0sZmlsZQ%3D%3D?line=50'>51</a> return mu_real + 1.0j * mu_imag
File ~/Downloads/Algorithm-Research/Student-Hub/Indy-Ng/.venv/lib/python3.11/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 ~/Downloads/Algorithm-Research/Student-Hub/Indy-Ng/.venv/lib/python3.11/site-packages/pennylane/interfaces/execution.py:600, 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)
595 if not device_batch_transform:
596 warnings.warn(
597 \"device batch transforms cannot be turned off with the new device interface.\",
598 UserWarning,
599 )
--> 600 tapes, post_processing = transform_program(tapes)
601 else:
602 # TODO: Remove once old device are removed
603 tapes, program_post_processing = transform_program(tapes)
File ~/Downloads/Algorithm-Research/Student-Hub/Indy-Ng/.venv/lib/python3.11/site-packages/pennylane/transforms/core/transform_program.py:429, in TransformProgram.__call__(self, tapes)
427 if self._argnums is not None and self._argnums[i] is not None:
428 tape.trainable_params = self._argnums[i][j]
--> 429 new_tapes, fn = transform(tape, *targs, **tkwargs)
430 execution_tapes.extend(new_tapes)
432 fns.append(fn)
File ~/Downloads/Algorithm-Research/Student-Hub/Indy-Ng/.venv/lib/python3.11/site-packages/pennylane/transforms/mitigate.py:512, in mitigate_with_zne(tape, scale_factors, folding, extrapolate, folding_kwargs, extrapolate_kwargs, reps_per_factor)
509 tape = tape.expand(stop_at=lambda op: not isinstance(op, QuantumScript))
510 script_removed = QuantumScript(tape.operations[tape.num_preps :])
--> 512 tapes = [
513 [folding(script_removed, s, **folding_kwargs) for _ in range(reps_per_factor)]
514 for s in scale_factors
515 ]
517 tapes = [tape_ for tapes_ in tapes for tape_ in tapes_] # flattens nested list
519 # if folding was a batch transform, ignore the processing function
File ~/Downloads/Algorithm-Research/Student-Hub/Indy-Ng/.venv/lib/python3.11/site-packages/pennylane/transforms/mitigate.py:513, in <listcomp>(.0)
509 tape = tape.expand(stop_at=lambda op: not isinstance(op, QuantumScript))
510 script_removed = QuantumScript(tape.operations[tape.num_preps :])
512 tapes = [
--> 513 [folding(script_removed, s, **folding_kwargs) for _ in range(reps_per_factor)]
514 for s in scale_factors
515 ]
517 tapes = [tape_ for tapes_ in tapes for tape_ in tapes_] # flattens nested list
519 # if folding was a batch transform, ignore the processing function
File ~/Downloads/Algorithm-Research/Student-Hub/Indy-Ng/.venv/lib/python3.11/site-packages/pennylane/transforms/mitigate.py:513, in <listcomp>(.0)
509 tape = tape.expand(stop_at=lambda op: not isinstance(op, QuantumScript))
510 script_removed = QuantumScript(tape.operations[tape.num_preps :])
512 tapes = [
--> 513 [folding(script_removed, s, **folding_kwargs) for _ in range(reps_per_factor)]
514 for s in scale_factors
515 ]
517 tapes = [tape_ for tapes_ in tapes for tape_ in tapes_] # flattens nested list
519 # if folding was a batch transform, ignore the processing function
File ~/Downloads/Algorithm-Research/Student-Hub/Indy-Ng/.venv/lib/python3.11/site-packages/mitiq/interface/conversions.py:305, in accept_qprogram_and_validate.<locals>.new_function(circuit, *args, **kwargs)
301 out_circuits = atomic_one_to_many_converter(cirq_circuit_modifier)(
302 circuit, *args, **kwargs
303 )
304 else:
--> 305 out_circuit = atomic_converter(cirq_circuit_modifier)(
306 circuit, *args, **kwargs
307 )
308 out_circuits = [out_circuit]
310 circuits_to_return = []
File ~/Downloads/Algorithm-Research/Student-Hub/Indy-Ng/.venv/lib/python3.11/site-packages/mitiq/interface/conversions.py:221, in atomic_converter.<locals>.qprogram_modifier(circuit, *args, **kwargs)
216 @wraps(cirq_circuit_modifier)
217 def qprogram_modifier(
218 circuit: QPROGRAM, *args: Any, **kwargs: Any
219 ) -> QPROGRAM:
220 # Convert to Mitiq representation.
--> 221 mitiq_circuit, input_circuit_type = convert_to_mitiq(circuit)
223 # Modify the Cirq circuit.
224 scaled_circuit = cirq_circuit_modifier(mitiq_circuit, *args, **kwargs)
File ~/Downloads/Algorithm-Research/Student-Hub/Indy-Ng/.venv/lib/python3.11/site-packages/mitiq/interface/conversions.py:125, in convert_to_mitiq(circuit)
123 mitiq_circuit = conversion_function(circuit)
124 except Exception:
--> 125 raise CircuitConversionError(
126 \"Circuit could not be converted to an internal Mitiq circuit. \"
127 \"This may be because the circuit contains custom gates or Pragmas \"
128 \"(pyQuil). If you think this is a bug or that this circuit should \"
129 \"be supported, you can open an issue at \"
130 \"https://github.com/unitaryfund/mitiq. \
\
Provided circuit has \"
131 f\"type {type(circuit)} and is:\
\
{circuit}\
\
Circuit types \"
132 f\"supported by Mitiq are \
{SUPPORTED_PROGRAM_TYPES}.\"
133 )
135 return mitiq_circuit, input_circuit_type
CircuitConversionError: Circuit could not be converted to an internal Mitiq circuit. This may be because the circuit contains custom gates or Pragmas (pyQuil). If you think this is a bug or that this circuit should be supported, you can open an issue at https://github.com/unitaryfund/mitiq.
Provided circuit has type <class 'pennylane.tape.qscript.QuantumScript'> and is:
<QuantumScript: wires=[3, 0, 1, 2], params=3>
Circuit types supported by Mitiq are
{'cirq': 'Circuit', 'pyquil': 'Program', 'qiskit': 'QuantumCircuit', 'braket': 'Circuit', 'pennylane': 'QuantumTape'}."
}
Here is my code:
import pennylane as qml
from pennylane import numpy as np
import time
def local_hadamard_test(weights, problem, l=None, lp=None, j=None, part=None):
ancilla_idx = problem.get_n_qubits()
# First Hadamard gate applied to the ancillary qubit.
qml.Hadamard(wires=ancilla_idx)
# For estimating the imaginary part of the coefficient "mu", we must add a "-i"
# phase gate.
if part == "Im" or part == "im":
qml.PhaseShift(-np.pi / 2, wires=ancilla_idx)
# Variational circuit generating a guess for the solution vector |x>
problem.variational_block(weights)
# Controlled application of the unitary component A_l of the problem matrix A.
problem.CA(ancilla_idx, l)
# Adjoint of the unitary U_b associated to the problem vector |b>.
# In this specific example Adjoint(U_b) = U_b.
problem.U_b()
# Controlled Z operator at position j. If j = -1, apply the identity.
if j != -1:
qml.CZ(wires=[ancilla_idx, j])
# Unitary U_b associated to the problem vector |b>.
problem.U_b()
# Controlled application of Adjoint(A_lp).
# In this specific example Adjoint(A_lp) = A_lp.
problem.CA(ancilla_idx, lp)
# Second Hadamard gate applied to the ancillary qubit.
qml.Hadamard(wires=ancilla_idx)
# Expectation value of Z for the ancillary qubit.
return qml.expval(qml.PauliZ(wires=ancilla_idx))
# Computes the mu coefficients
def mu(weights, local_hadamard_test, problem, l=None, lp=None, j=None):
"""Generates the coefficients to compute the "local" cost function C_L."""
mu_real = local_hadamard_test(weights, problem, l=l, lp=lp, j=j, part="Re")
mu_imag = local_hadamard_test(weights, problem, l=l, lp=lp, j=j, part="Im")
return mu_real + 1.0j * mu_imag
def psi_norm(weights, c, local_hadamard_test, problem):
"""Returns the normalization constant <psi|psi>, where |psi> = A |x>."""
norm = 0.0
for l in range(0, len(c)):
for lp in range(0, len(c)):
norm = norm + c[l] * np.conj(c[lp]) * mu(weights, local_hadamard_test, problem, l, lp, -1)
return abs(norm)
def cost_loc(problem, weights, local_hadamard_test):
"""Local version of the cost function. Tends to zero when A|x> is proportional to |b>."""
c, _ = problem.get_coeffs()
n_qubits = problem.get_n_qubits()
mu_sum = 0.0
for l in range(0, len(c)):
for lp in range(0, len(c)):
for j in range(0, n_qubits):
mu_sum = mu_sum + c[l] * np.conj(c[lp]) * mu(weights, local_hadamard_test, problem, l, lp, j)
mu_sum = abs(mu_sum)
# Cost function C_L
res = 0.5 - 0.5 * mu_sum / (n_qubits * psi_norm(weights, c, local_hadamard_test, problem))
return res
# convert matrix A encoded as a string (eg. "IZZ") into qml code
def A_to_code (idx, ancilla_idx, terms, offset=0):
if idx < 0:
raise ValueError("Index of linear combination must be >= 0.")
target_pauli = list(terms[idx])
order_idx = offset
for i in range(len(target_pauli)):
if target_pauli[i] == 'I':
order_idx += 1
None
if target_pauli[i] == 'X':
qml.CNOT(wires = (ancilla_idx, order_idx))
order_idx += 1
if target_pauli[i] == 'Y':
qml.CY(wires = (ancilla_idx, order_idx))
order_idx += 1
if target_pauli[i] == 'Z':
qml.CZ(wires = (ancilla_idx, order_idx))
order_idx += 1
import functools as ft
pauli_dict = {"I": qml.Identity.compute_matrix(), "X": qml.PauliX.compute_matrix(), "Y": qml.PauliY.compute_matrix(), "Z": qml.PauliZ.compute_matrix()}
def A_to_num (n_qubits: int, coefs: np.tensor, terms):
"""
Given an array of coeffs c and an array of A_l formatted as a list of strings, return A
@params
coefs (eg. [1, 0.2, 0.2])
terms (eg. ["III", "XZI", "XII"])
returns an np.array
"""
if len(coefs) != len(terms):
raise ValueError("Number of coefficients does not match number of terms.")
if n_qubits <= 0:
raise ValueError("Number of qubits is not a number greater than 0.")
terms_len = len(terms)
for i in range(terms_len):
if len(terms[i]) != n_qubits:
raise ValueError("Number of terms in each Pauli gate combination must be the same as number of qubits.")
dim = 2**n_qubits
mat = np.zeros((dim, dim), dtype=np.complex64)
for (c, pauli) in zip(coefs, terms):
pauli = [pauli_dict[key] for key in pauli]
if pauli == ["I"]:
mat += c * ft.reduce(np.kron, pauli)
else:
mat += c * ft.reduce(np.kron, pauli)
return mat
# these classes encode the linear system we are trying to solve
from abc import ABC, abstractmethod
class Problem(ABC):
def __init__(self, n_qubits, c, A_terms) -> None:
super().__init__()
self.n_qubits = n_qubits
self.A_num = A_to_num(n_qubits, c, A_terms)
self.A_terms = A_terms
# normalize c
self.c = np.array(c) / np.linalg.norm(self.A_num, ord=2)
# Total number of qubits; here we add an ancillary qubit
self.tot_qubits = self.n_qubits + 1
# Index of ancillary qubit (Python lists are 0-indexed)
self.ancilla_idx = self.n_qubits
@abstractmethod
def get_coeffs():
"""gets c, A_l"""
pass
@abstractmethod
def get_n_qubits():
"""gets number of qubits of your problem"""
pass
@abstractmethod
def U_b():
"""Unitary matrix rotating the ground state to the problem vector |b> = U_b |0>."""
pass
@abstractmethod
def CA(idx):
pass
@abstractmethod
def variational_block(weights):
pass
class ToyProblem(Problem):
def __init__(self, n_qubits):
c = [1, 0.25]
A_terms = ["III", "IIZ"]
super().__init__(n_qubits, c, A_terms)
self.param_shape = n_qubits
def get_coeffs(self):
return self.c, self.A_terms
def get_n_qubits(self):
return self.n_qubits
def U_b(self):
"""Unitary matrix rotating the ground state to the problem vector |b> = U_b |0>."""
[qml.Hadamard(wires=i) for i in [0,1]]
def CA(self, ancilla_idx, idx, offset=0):
A_to_code(idx, ancilla_idx=ancilla_idx, terms=self.A_terms, offset=offset)
def variational_block(self, weights, offset=0):
[qml.RY(phi=weights[i], wires=i+offset) for i in range(self.n_qubits)]
#############
n_qubits = 3
dev_mu = qml.device("default.qubit", wires=n_qubits+1)
from mitiq.zne.scaling import fold_global
from mitiq.zne.inference import RichardsonFactory
from pennylane.transforms import mitigate_with_zne
extrapolate = RichardsonFactory.extrapolate
scale_factors = [1, 2, 3]
local_hadamard_test = qml.QNode(local_hadamard_test, dev_mu, interface="autograd")
local_hadamard_test = mitigate_with_zne(local_hadamard_test, scale_factors, fold_global, extrapolate) # comment this line out to remove the error mitigation
problem = ToyProblem(3)
opt = qml.GradientDescentOptimizer(0.1)
w = np.random.randn(problem.param_shape, requires_grad=True)
start = time.time()
for it in range(10):
# w, cost = opt.step_and_cost(cost_agg, w)
w, cost = opt.step_and_cost(lambda w: cost_loc(problem, w, local_hadamard_test), w)
print("Step {:3d} Cost_L = {:9.7f}".format(it, cost), flush=True)
it += 1
print(f"Training time: {time.time() - start}s")
qml.about()
:
Name: PennyLane
Version: 0.33.1
Summary: PennyLane is a Python quantum machine learning library by Xanadu Inc.
Home-page: https://github.com/PennyLaneAI/pennylane
Author:
Author-email:
License: Apache License 2.0
Location: /Users/bigsad/Downloads/Algorithm-Research/Student-Hub/Indy-Ng/.venv/lib/python3.11/site-packages
Requires: appdirs, autograd, autoray, cachetools, networkx, numpy, pennylane-lightning, requests, rustworkx, scipy, semantic-version, toml, typing-extensions
Required-by: PennyLane-Lightning, PennyLane-qiskit
Platform info: macOS-12.6-x86_64-i386-64bit
Python version: 3.11.6
Numpy version: 1.23.5
Scipy version: 1.10.1
Installed devices:
- default.gaussian (PennyLane-0.33.1)
- default.mixed (PennyLane-0.33.1)
- default.qubit (PennyLane-0.33.1)
- default.qubit.autograd (PennyLane-0.33.1)
- default.qubit.jax (PennyLane-0.33.1)
- default.qubit.legacy (PennyLane-0.33.1)
- default.qubit.tf (PennyLane-0.33.1)
- default.qubit.torch (PennyLane-0.33.1)
- default.qutrit (PennyLane-0.33.1)
- null.qubit (PennyLane-0.33.1)
- lightning.qubit (PennyLane-Lightning-0.33.1)
- qiskit.aer (PennyLane-qiskit-0.33.1)
- qiskit.basicaer (PennyLane-qiskit-0.33.1)
- qiskit.ibmq (PennyLane-qiskit-0.33.1)
- qiskit.ibmq.circuit_runner (PennyLane-qiskit-0.33.1)
- qiskit.ibmq.sampler (PennyLane-qiskit-0.33.1)
- qiskit.remote (PennyLane-qiskit-0.33.1)