Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reconstruct expectation values of SparsePauliOp operators in notebooks #523

Merged
merged 2 commits into from
Apr 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"outputs": [],
"source": [
"from qiskit import QuantumCircuit\n",
"from qiskit.quantum_info import PauliList\n",
"from qiskit.quantum_info import SparsePauliOp\n",
"\n",
"from circuit_knitting.cutting import (\n",
" partition_problem,\n",
Expand All @@ -44,9 +44,9 @@
"circuit = QuantumCircuit(2)\n",
"circuit.h(0)\n",
"circuit.cx(0, 1)\n",
"observables = PauliList([\"ZZ\"])\n",
"observable = SparsePauliOp([\"ZZ\"])\n",
"partitioned_problem = partition_problem(\n",
" circuit=circuit, partition_labels=\"AB\", observables=observables\n",
" circuit=circuit, partition_labels=\"AB\", observables=observable.paulis\n",
")\n",
"subcircuits = partitioned_problem.subcircuits\n",
"subobservables = partitioned_problem.subobservables"
Expand Down Expand Up @@ -132,7 +132,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.13"
"version": "3.11.8"
}
},
"nbformat": 4,
Expand Down

Large diffs are not rendered by default.

65 changes: 30 additions & 35 deletions docs/circuit_cutting/how-tos/how_to_specify_cut_wires.ipynb

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,7 @@
"id": "452cd19e",
"metadata": {},
"source": [
"### Specify some observables\n",
"\n",
"Currently, only `Pauli` observables with phase equal to 1 are supported. Full support for `SparsePauliOp` is expected in CKT v0.7.0."
"### Specify an observable"
]
},
{
Expand All @@ -67,9 +65,9 @@
"metadata": {},
"outputs": [],
"source": [
"from qiskit.quantum_info import PauliList\n",
"from qiskit.quantum_info import SparsePauliOp\n",
"\n",
"observables = PauliList([\"ZZII\", \"IZZI\", \"IIZZ\", \"XIXI\", \"ZIZZ\", \"IXIX\"])"
"observable = SparsePauliOp([\"ZZII\", \"IZZI\", \"-IIZZ\", \"XIXI\", \"ZIZZ\", \"IXIX\"])"
]
},
{
Expand Down Expand Up @@ -136,7 +134,7 @@
"name": "stdout",
"output_type": "stream",
"text": [
"Transpiled circuit depth: 101\n"
"Transpiled circuit depth: 30\n"
]
}
],
Expand All @@ -148,7 +146,7 @@
")\n",
"\n",
"transpiled_qc = pass_manager.run(circuit)\n",
"print(f\"Transpiled circuit depth: {transpiled_qc.depth()}\")"
"print(f\"Transpiled circuit depth: {transpiled_qc.depth(lambda x: len(x[1]) >= 2)}\")"
]
},
{
Expand Down Expand Up @@ -226,7 +224,9 @@
"\n",
"`generate_cutting_experiments` accepts a circuit containing `TwoQubitQPDGate` instances and observables as a `PauliList`.\n",
"\n",
"To simulate the expectation value of the full-sized circuit, many subexperiments are generated from the decomposed gates' joint quasiprobability distribution and then executed on one or more backends. The number of samples taken from the distribution is controlled by `num_samples`, and one combined coefficient is given for each unique sample. For more information on how the coefficients are calculated, refer to the [explanatory material](../explanation/index.rst)."
"To simulate the expectation value of the full-sized circuit, many subexperiments are generated from the decomposed gates' joint quasiprobability distribution and then executed on one or more backends. The number of samples taken from the distribution is controlled by `num_samples`, and one combined coefficient is given for each unique sample. For more information on how the coefficients are calculated, refer to the [explanatory material](../explanation/index.rst).\n",
"\n",
"**Note:** The ``observables`` kwarg to `generate_cutting_experiments` is of type `PauliList`. Observable term coefficients and phases are ignored during decomposition of the problem and execution of the subexperiments. They may be re-applied during reconstruction of the expectation value."
]
},
{
Expand All @@ -241,7 +241,7 @@
"\n",
"# Generate the subexperiments and sampling coefficients\n",
"subexperiments, coefficients = generate_cutting_experiments(\n",
" circuits=qpd_circuit, observables=observables, num_samples=np.inf\n",
" circuits=qpd_circuit, observables=observable.paulis, num_samples=np.inf\n",
")"
]
},
Expand Down Expand Up @@ -295,8 +295,8 @@
"name": "stdout",
"output_type": "stream",
"text": [
"Original circuit depth after transpile: 101\n",
"QPD subexperiment depth after transpile: 26\n"
"Original circuit depth after transpile: 30\n",
"QPD subexperiment depth after transpile: 7\n"
]
},
{
Expand All @@ -315,8 +315,12 @@
"# Transpile the decomposed circuit to the same layout\n",
"transpiled_qpd_circuit = pass_manager.run(subexperiments[100])\n",
"\n",
"print(f\"Original circuit depth after transpile: {transpiled_qc.depth()}\")\n",
"print(f\"QPD subexperiment depth after transpile: {transpiled_qpd_circuit.depth()}\")\n",
"print(\n",
" f\"Original circuit depth after transpile: {transpiled_qc.depth(lambda x: len(x[1]) >= 2)}\"\n",
")\n",
"print(\n",
" f\"QPD subexperiment depth after transpile: {transpiled_qpd_circuit.depth(lambda x: len(x[1]) >= 2)}\"\n",
")\n",
"transpiled_qpd_circuit.draw(\"mpl\", scale=0.8, idle_wires=False, fold=-1)"
]
},
Expand Down Expand Up @@ -374,7 +378,7 @@
"source": [
"### Reconstruct the expectation values\n",
"\n",
"Use the subexperiment results, subobservables, and sampling coefficients to reconstruct the expectation value of the original circuit."
"Reconstruct expectation values for each observable term and combine them to reconstruct the expectation value for the original observable."
]
},
{
Expand All @@ -389,16 +393,18 @@
"reconstructed_expvals = reconstruct_expectation_values(\n",
" results,\n",
" coefficients,\n",
" observables,\n",
")"
" observable.paulis,\n",
")\n",
"# Reconstruct final expectation value\n",
"final_expval = np.dot(reconstructed_expvals, observable.coeffs)"
]
},
{
"cell_type": "markdown",
"id": "3fc2327f",
"metadata": {},
"source": [
"### Compare reconstructed expectation values to exact expectation values from the original circuit"
"### Compare the reconstructed expectation values with the exact expectation value from the original circuit and observable"
]
},
{
Expand All @@ -411,31 +417,23 @@
"name": "stdout",
"output_type": "stream",
"text": [
"Reconstructed expectation values: [0.52972412, 0.5692749, 0.37542725, -0.20349121, 0.25061035, -0.24511719]\n",
"Exact expectation values: [0.50983039, 0.56127511, 0.36167086, -0.23006544, 0.23416169, -0.20855487]\n",
"Errors in estimation: [0.01989373, 0.00799979, 0.01375639, 0.02657423, 0.01644866, -0.03656231]\n",
"Relative errors in estimation: [0.03902028, 0.01425288, 0.03803565, -0.11550725, 0.07024487, 0.17531269]\n"
"Reconstructed expectation value: 0.55578613\n",
"Exact expectation value: 0.50497603\n",
"Error in estimation: 0.0508101\n",
"Relative error in estimation: 0.10061884\n"
]
}
],
"source": [
"from qiskit_aer.primitives import Estimator\n",
"\n",
"estimator = Estimator(run_options={\"shots\": None}, approximation=True)\n",
"exact_expvals = (\n",
" estimator.run([circuit] * len(observables), list(observables)).result().values\n",
")\n",
"print(\n",
" f\"Reconstructed expectation values: {[np.round(reconstructed_expvals[i], 8) for i in range(len(exact_expvals))]}\"\n",
")\n",
"print(\n",
" f\"Exact expectation values: {[np.round(exact_expvals[i], 8) for i in range(len(exact_expvals))]}\"\n",
")\n",
"print(\n",
" f\"Errors in estimation: {[np.round(reconstructed_expvals[i]-exact_expvals[i], 8) for i in range(len(exact_expvals))]}\"\n",
")\n",
"exact_expval = estimator.run(circuit, observable).result().values[0]\n",
"print(f\"Reconstructed expectation value: {np.real(np.round(final_expval, 8))}\")\n",
"print(f\"Exact expectation value: {np.round(exact_expval, 8)}\")\n",
"print(f\"Error in estimation: {np.real(np.round(final_expval-exact_expval, 8))}\")\n",
"print(\n",
" f\"Relative errors in estimation: {[np.round((reconstructed_expvals[i]-exact_expvals[i]) / exact_expvals[i], 8) for i in range(len(exact_expvals))]}\"\n",
" f\"Relative error in estimation: {np.real(np.round((final_expval-exact_expval) / exact_expval, 8))}\"\n",
")"
]
}
Expand All @@ -456,7 +454,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.0"
"version": "3.11.8"
}
},
"nbformat": 4,
Expand Down

Large diffs are not rendered by default.

18 changes: 9 additions & 9 deletions docs/circuit_cutting/tutorials/04_automatic_cut_finding.ipynb

Large diffs are not rendered by default.