From 665a7948506eeaea2561d83f52dba151c8bb5929 Mon Sep 17 00:00:00 2001 From: Johnnie Gray Date: Mon, 3 Feb 2025 22:46:12 -0800 Subject: [PATCH] add circuit to mpo example --- docs/examples/ex_circuit_to_mpo.ipynb | 398 ++++++++++++++++++++++++++ docs/index_examples.md | 1 + 2 files changed, 399 insertions(+) create mode 100644 docs/examples/ex_circuit_to_mpo.ipynb diff --git a/docs/examples/ex_circuit_to_mpo.ipynb b/docs/examples/ex_circuit_to_mpo.ipynb new file mode 100644 index 00000000..f23a000d --- /dev/null +++ b/docs/examples/ex_circuit_to_mpo.ipynb @@ -0,0 +1,398 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "54dd40bd-9ded-476e-8960-b23b44ea3a83", + "metadata": {}, + "source": [ + "# Converting a Circuit to an MPO\n", + "\n", + "This example shows how to convert a quantum circuit (that is sufficiently shallow) into a matrix product operator (MPO)." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "a7e988e7-1174-4bb8-8018-5cc30303f91d", + "metadata": {}, + "outputs": [], + "source": [ + "%config InlineBackend.figure_formats = ['svg']\n", + "import quimb.tensor as qtn" + ] + }, + { + "cell_type": "markdown", + "id": "0b269b50-355a-46e1-8b8b-67007fd93b00", + "metadata": {}, + "source": [ + "First we generate some random 1D gates:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "a9043b3e-54fb-4187-b386-f640eaf1a5d4", + "metadata": {}, + "outputs": [], + "source": [ + "gates = qtn.circuit_gen.gates_1D_rand(10, depth=8, seed=42)" + ] + }, + { + "cell_type": "markdown", + "id": "0305b1c2-9fab-4187-ac56-934b9ae08beb", + "metadata": {}, + "source": [ + "Then we construct the [`Circuit`](quimb.tensor.circuit.Circuit):" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "6fb9dd46-d562-431d-af68-235c88db8521", + "metadata": {}, + "outputs": [], + "source": [ + "circ = qtn.Circuit.from_gates(\n", + " gates,\n", + " # this ensure each tensor belongs to one site only\n", + " gate_contract=\"split-gate\",\n", + " # just for cleaner tags\n", + " tag_gate_numbers=False,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "e515545c-90d1-4603-922e-80d80f730d2b", + "metadata": {}, + "source": [ + "Next we extract just the unitary part of the tensor network:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "b1cb8fcf-6507-4798-9e02-118899ff8cdf", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "" + ], + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "tn_uni = circ.get_uni()\n", + "tn_uni.draw(tn_uni.site_tags, show_tags=False)" + ] + }, + { + "cell_type": "markdown", + "id": "a4e13ea9-4b67-4fe7-9eb9-4d077ef66ff2", + "metadata": {}, + "source": [ + "## By direct contraction:\n", + "\n", + "Then we contract each group of tensor associate with each site (color above):" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "aeea7aea-2098-44f2-b895-f81b01e22c53", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "" + ], + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "for site in tn_uni.site_tags:\n", + " tn_uni ^= site\n", + "\n", + "tn_uni.draw(tn_uni.site_tags, show_tags=False)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "cf259f17-2032-46cd-be97-d29a0a2f73e7", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "" + ], + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "tn_uni.fuse_multibonds_()\n", + "tn_uni.draw(tn_uni.site_tags, show_tags=False)" + ] + }, + { + "cell_type": "markdown", + "id": "b37b3d70-0291-4b23-bc7a-19edb0620989", + "metadata": {}, + "source": [ + "Now it has the form of an MPO, we can cast it the actual [`MatrixProductOperator`](quimb.tensor.tensor_1d.MatrixProductOperator) class:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "2b807f83-4fb0-48d0-8cd9-0f088fbab22b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
MatrixProductOperator(tensors=10, indices=29, L=10, max_bond=256)
Tensor(shape=(2, 256, 2), inds=[b0, _595a34AAAAf, k0], tags={U3, I0, CZ}),backend=numpy, dtype=complex128, data=...
Tensor(shape=(2, 256, 256, 2), inds=[b1, _595a34AAAAf, _595a34AAAAp, k1], tags={U3, I1, CZ}),backend=numpy, dtype=complex128, data=...
Tensor(shape=(2, 256, 256, 2), inds=[b2, _595a34AAAAQ, _595a34AAAAp, k2], tags={U3, I2, CZ}),backend=numpy, dtype=complex128, data=...
Tensor(shape=(2, 256, 256, 2), inds=[b3, _595a34AAAAQ, _595a34AAAAV, k3], tags={U3, I3, CZ}),backend=numpy, dtype=complex128, data=...
Tensor(shape=(2, 256, 256, 2), inds=[b4, _595a34AAAAV, _595a34AAAAu, k4], tags={U3, I4, CZ}),backend=numpy, dtype=complex128, data=...
Tensor(shape=(2, 256, 256, 2), inds=[b5, _595a34AAAAa, _595a34AAAAu, k5], tags={U3, I5, CZ}),backend=numpy, dtype=complex128, data=...
Tensor(shape=(2, 256, 256, 2), inds=[b6, b, _595a34AAAAa, k6], tags={U3, I6, CZ}),backend=numpy, dtype=complex128, data=...
Tensor(shape=(2, 256, 256, 2), inds=[b7, b, _595a34AAABi, k7], tags={U3, I7, CZ}),backend=numpy, dtype=complex128, data=...
Tensor(shape=(2, 256, 256, 2), inds=[b8, _595a34AAABi, _595a34AAABs, k8], tags={U3, I8, CZ}),backend=numpy, dtype=complex128, data=...
Tensor(shape=(2, 256, 2), inds=[b9, _595a34AAABs, k9], tags={U3, I9, CZ}),backend=numpy, dtype=complex128, data=...
" + ], + "text/plain": [ + "MatrixProductOperator(tensors=10, indices=29, L=10, max_bond=256)" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "tn_uni.view_as_(\n", + " qtn.MatrixProductOperator,\n", + " cyclic=False,\n", + " L=circ.N,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "0ce26b33-a08f-435d-b25d-2ee9f50cdc64", + "metadata": {}, + "source": [ + "This allows us to call MPO specific methods." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "2b6b7eec-f044-419b-9d35-9792335876a6", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "│4│16│64│128│125│128│64│16│4│\n", + "●─<──<──<━━━<━━━<━━━<──<──<─<\n", + "│ │ │ │ │ │ │ │ │ │\n" + ] + } + ], + "source": [ + "tn_uni.compress(cutoff=1e-6, cutoff_mode=\"rel\")\n", + "tn_uni.show()" + ] + }, + { + "cell_type": "markdown", + "id": "04086020-88f6-4624-9ae2-056e9265e85f", + "metadata": {}, + "source": [ + "## By fitting\n", + "\n", + "An alternative approach that will scale better, especially if a fixed `max_bond` is known ahead of time is to directly fit a low-rank 1D TN to the unitary:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "1682edfd-f334-4d30-9a50-662aebe6c3cb", + "metadata": {}, + "outputs": [], + "source": [ + "# get a fresh (uncontracted) TN representation of the circuit\n", + "tn_uni = circ.get_uni()" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "0322b9ec-c8a1-4495-b686-2b92b2ec9232", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "max_tdiff=8.62e-07: 8%|8 | 8/100 [00:01<00:14, 6.56it/s]\n" + ] + } + ], + "source": [ + "# compress via fitting:\n", + "tnc = qtn.tensor_network_1d_compress(\n", + " tn_uni,\n", + " max_bond=32,\n", + " cutoff=0.0,\n", + " method=\"fit\",\n", + " bsz=2, # bsz=1 is cheaper per sweep, but possibly slower to converge\n", + " max_iterations=100,\n", + " tol=1e-6,\n", + " progbar=True,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "1a7a702f-2b2e-4c20-9182-0fdf19ede77f", + "metadata": {}, + "source": [ + "Compute relative frobenius norm error:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "d1be1c46-6ae9-4429-a9f5-60c1990cb0e6", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "np.float64(0.09292853617632899)" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "tnc.distance_normalized(tn_uni)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "55835bc5-735e-4469-9016-e6df88ffe78d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
MatrixProductOperator(tensors=10, indices=29, L=10, max_bond=32)
Tensor(shape=(32, 32, 2, 2), inds=[_595a34AAAHg, _595a34AAAHh, b6, k6], tags={U3, I6, CZ}),backend=numpy, dtype=complex128, data=...
Tensor(shape=(32, 16, 2, 2), inds=[_595a34AAAHg, _595a34AAAHi, b7, k7], tags={U3, I7, CZ}),backend=numpy, dtype=complex128, data=...
Tensor(shape=(32, 16, 2, 2), inds=[_595a34AAAHj, _595a34AAAHk, b2, k2], tags={U3, I2, CZ}),backend=numpy, dtype=complex128, data=...
Tensor(shape=(32, 32, 2, 2), inds=[_595a34AAAHj, _595a34AAAHl, b3, k3], tags={U3, I3, CZ}),backend=numpy, dtype=complex128, data=...
Tensor(shape=(32, 32, 2, 2), inds=[_595a34AAAHl, _595a34AAAHm, b4, k4], tags={U3, I4, CZ}),backend=numpy, dtype=complex128, data=...
Tensor(shape=(32, 32, 2, 2), inds=[_595a34AAAHh, _595a34AAAHm, b5, k5], tags={U3, I5, CZ}),backend=numpy, dtype=complex128, data=...
Tensor(shape=(4, 2, 2), inds=[_595a34AAAHn, b0, k0], tags={U3, I0, CZ}),backend=numpy, dtype=complex128, data=array([[[-0.61604372+2.40819678e-17j, 0.25919934+2.30325559e-01j],\n", + " [-0.05734167-3.41258809e-01j, 0.32784968-5.22517871e-01j]],\n", + "\n", + " [[-0.65808868-2.40819678e-17j, 0.1087671 -2.34730203e-01j],\n", + " [-0.24610535+7.93029015e-02j, -0.07633141+6.53687583e-01j]],\n", + "\n", + " [[-0.10613633-2.75222489e-17j, -0.079237 -6.94514262e-01j],\n", + " [ 0.59143999-3.73011592e-01j, -0.06566129-8.28022244e-02j]],\n", + "\n", + " [[ 0.41969573-3.59768069e-17j, 0.53097181-2.05615319e-01j],\n", + " [-0.32049612-4.70893724e-01j, 0.34493524+2.37081819e-01j]]])
Tensor(shape=(4, 16, 2, 2), inds=[_595a34AAAHn, _595a34AAAHk, b1, k1], tags={U3, I1, CZ}),backend=numpy, dtype=complex128, data=...
Tensor(shape=(16, 4, 2, 2), inds=[_595a34AAAHi, _595a34AAAHo, b8, k8], tags={U3, I8, CZ}),backend=numpy, dtype=complex128, data=...
Tensor(shape=(4, 2, 2), inds=[_595a34AAAHo, b9, k9], tags={U3, I9, CZ}),backend=numpy, dtype=complex128, data=array([[[ 10.46656957-0.j , -8.28078722+3.04946084j],\n", + " [ -7.62832633-4.40215621j, -10.30453618-1.75636178j]],\n", + "\n", + " [[ -8.43641373-0.j , 3.89935365-8.36740555j],\n", + " [ -7.84146634-4.86507481j, -7.0820341 +4.61468449j]],\n", + "\n", + " [[ -5.17145175-0.j , -6.68877199+4.18585282j],\n", + " [ 5.20979118-5.94800211j, -0.82457769+5.10399981j]],\n", + "\n", + " [[ 7.02674189-0.j , 3.03830461-4.03398835j],\n", + " [ 3.26177468-3.84660945j, 1.56292165+6.85004125j]]])
" + ], + "text/plain": [ + "MatrixProductOperator(tensors=10, indices=29, L=10, max_bond=32)" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# againt cast as MPO if we want more methods\n", + "tnc.view_as_(\n", + " qtn.MatrixProductOperator,\n", + " cyclic=False,\n", + " L=circ.N,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "4a142d78-2d14-4ae7-b39e-5f703a85c260", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "│4│16│32│32│32│32│32│16│4│\n", + ">─>──>──>──>──>──>──>──>─●\n", + "│ │ │ │ │ │ │ │ │ │\n" + ] + } + ], + "source": [ + "tnc.show()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/index_examples.md b/docs/index_examples.md index 98e7f354..c4bd4bd0 100644 --- a/docs/index_examples.md +++ b/docs/index_examples.md @@ -19,5 +19,6 @@ ./examples/ex_quimb_within_torch ./examples/ex_quimb_within_jax_flax_optax ./examples/ex_tn_tensor_fitting +./examples/ex_circuit_to_mpo ./examples/schematic-demo ```