qsample
================
<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->
## Install
pip install qsample
## Prerequisites
- python3
- pdflatex (for circuit rendering)
## When to use
- For QEC protocols with in-sequence measurements and feed-forward of
measurement information
- Apply circuit-level incoherent Pauli noise at low physical error rates
(i.e. high fidelity physical operations)
- Simulate and sample protocol execution over ranges of varying physical
error rates, using customizable callbacks
## Getting started
``` python
import qsample as qs # import qsample
import matplotlib.pyplot as plt # import matplotlib for visualization of results
```
First, we need to define a quantum protocol of which we would like to
know the logical error rate. In `qsample` a protocol is represented as a
graph of quantum
[`Circuit`](https://dpwinter.github.io/qsample/circuit.html#circuit)s as
nodes and transition `checks` (to be checked at samplingt ime) as edges.
Example: To sample logical error rates of an error-corrected quantum
state teleportation protocol, we define the teleportation circuit which
sends the state of the first to the third qubit.
``` python
teleport = qs.Circuit([{"init": {0, 1, 2}},
{"H": {1}},
{"CNOT": {(1, 2)}},
{"CNOT": {(0, 1)}},
{"H": {0}},
{"measure": {0, 1}}])
teleport.draw()
```
![](index_files/figure-commonmark/cell-3-output-1.svg)
Additionally, we need a circuit to (perfectly) measure the third qubit
after running `teleport`. If the outcome of this measurement is 0
(corresponding to the initially prepared $|0\rangle$ state of qubit 1)
the teleportation succeded. If the outcome is 1 however, we want to
count a logical failure of this protocol. Let’s create a circuit for
this measurement and let’s assume we can perform this measurement
without noise.
``` python
meas = qs.Circuit([{"measure": {2}}], noisy=False)
```
Between the `teleport` and `meas` circuits apply a correction to qubit 3
conditioned on the measurement outcome (syndrome) of the teleportation
circuit. We define the lookup function `lut`
``` python
def lut(syn):
op = {0: 'I', 1: 'X', 2: 'Z', 3: 'Y'}[syn]
return qs.Circuit([{op: {2}}], noisy=False)
```
Finally, define the circuit sequence and transition logic together
within a
[`Protocol`](https://dpwinter.github.io/qsample/protocol.html#protocol)
object. Note that protocols must always commence with a unique *START*
node and terminate at a unique *FAIL* node, where the latter expresses a
logical failure event.
``` python
tele_proto = qs.Protocol(check_functions={'lut': lut})
tele_proto.add_nodes_from(['tele', 'meas'], circuits=[teleport, meas])
tele_proto.add_edge('START', 'tele', check='True')
tele_proto.add_edge('tele', 'COR', check='lut(tele[-1])')
tele_proto.add_edge('COR', 'meas', check='True')
tele_proto.add_edge('meas', 'FAIL', check='meas[-1] == 1')
tele_proto.draw(figsize=(8,5))
```
![](index_files/figure-commonmark/cell-6-output-1.png)
Notice that we do not define any initial circuit for the correction
*COR* but pass our lookup function to the `check_functions` dictionary,
which makes it accessible inside the `check` transition statements
(edges) between circuits. This way we can dynamically insert circuits
into the protocol at execution time.
After the protocol has been defined we can repeatedly execute
(i.e. sample) it in the presence of incoherent noise. Let’s say we are
interested in the logical error rates for physical error rates on all 1-
and 2-qubit gates of $p_{phy}=10^{-5}, \dots, 10^{-1}$, and $0.5$. The
corresponding noise model is called
[`E1`](https://dpwinter.github.io/qsample/noise.html#e1) in qsample. The
groups of all 1- and 2-qubit gates are indexed by the key *q* in
[`E1`](https://dpwinter.github.io/qsample/noise.html#e1). Other noise
models (and their parameters) are described in the documentation.
``` python
err_model = qs.noise.E1
err_params = {'q': [1e-5, 1e-4, 1e-3, 1e-2, 1e-1, 0.5]}
```
We are ready to sample. As our protocol only contains Clifford gates
let’s choose the
[`StabilizerSimulator`](https://dpwinter.github.io/qsample/sim.stabilizer.html#stabilizersimulator),
as well as the
[`PlotStats`](https://dpwinter.github.io/qsample/callbacks.html#plotstats)
callback for plotting the resulting logical error rate as function of
$p_{phy}$.
``` python
sam = qs.DirectSampler(protocol=tele_proto, simulator=qs.StabilizerSimulator, err_model=err_model, err_params=err_params)
sam.run(n_shots=20000, callbacks=[qs.callbacks.PlotStats()])
```
p=('1.00e-05',): 0%| | 0/20000 [00:00<?, ?it/s]
p=('1.00e-04',): 0%| | 0/20000 [00:00<?, ?it/s]
p=('1.00e-03',): 0%| | 0/20000 [00:00<?, ?it/s]
p=('1.00e-02',): 0%| | 0/20000 [00:00<?, ?it/s]
p=('1.00e-01',): 0%| | 0/20000 [00:00<?, ?it/s]
p=('5.00e-01',): 0%| | 0/20000 [00:00<?, ?it/s]
![](index_files/figure-commonmark/cell-8-output-7.png)
Notice, that at low error rates
[`DirectSampler`](https://dpwinter.github.io/qsample/sampler.direct.html#directsampler)
performs badly, i.e. the error bars become very large, as most of the
time the protocol is executed error free and, consequently, logical
errors are measured infrequently. In this regime it is much more
efficient to use an importance sampling strategy to avoid fault-free
protocol execution and instead put more emphasis on execution with at
least one fault happening. This approach is implemented in the
[`SubsetSampler`](https://dpwinter.github.io/qsample/sampler.subset.html#subsetsampler)
class. We only need to specify **two additional parameter** `p_max`
which specifies the $p_{phy}$ at which sampling takes place, and `L`,
the length of the longest possible fault-free path. The parameter
`p_max` must be chosen experimentally by repeated sampling and observing
which subsets have the largest impact on the failure rate. We must
always choose a value such that the subset occurence probability has an
exponentially falling shape. Only in this case are the scaling of the
sampler results valid. Below we see that for the teleportation circuit
`p_max`-values of 0.01 and 0.1 are still okay, while 0.3 would be
problematic. For more information refer to the linked publication.
``` python
for p_phy in [0.01, 0.1, 0.3]:
Aws = qs.math.subset_probs(teleport, err_model(), p_phy)
plt.figure()
plt.title("Subset occurence prob. $A_w$ at $p_{phy}$=%.2f" % p_phy)
plt.bar(list(map(str,Aws.keys())), Aws.values())
plt.ylabel("$A_w$")
plt.xlabel("Subsets")
```
![](index_files/figure-commonmark/cell-9-output-1.png)
![](index_files/figure-commonmark/cell-9-output-2.png)
![](index_files/figure-commonmark/cell-9-output-3.png)
Let’s choose a $p_{max}=0.1$ for the same error model as before and
start sampling. (Note the significant difference in the number of
samples).
``` python
ss_sam = qs.SubsetSampler(protocol=tele_proto, simulator=qs.StabilizerSimulator, p_max={'q': 0.1}, err_model=err_model, err_params=err_params, L=3)
ss_sam.run(1000, callbacks=[qs.callbacks.PlotStats()])
```
p=('1.00e-01',): 0%| | 0/1000 [00:00<?, ?it/s]
![](index_files/figure-commonmark/cell-10-output-2.png)
The sampling results are internally stored by the
[`SubsetSampler`](https://dpwinter.github.io/qsample/sampler.subset.html#subsetsampler)
in a [`Tree`](https://dpwinter.github.io/qsample/sampler.tree.html#tree)
data structure. In the tree we can also see why we chose `L`=3, as there
are three circuits in the fault-free path sequence.
``` python
ss_sam.tree.draw(verbose=True)
```
![](index_files/figure-commonmark/cell-11-output-1.png)
We see that only the teleportation protocol has fault weight subsets,
while the *meas* and *COR* circuits are noise-free (i.e. they only have
the 0-subset). The leaf nodes *FAIL* and *None* represent logical
failure and successful teleportation events, respectively. $\delta$
represents the missing subsets which have not been sampled and which
result in the upper bound on the failure rate.
Finally, let’s compare the results of
[`DirectSampler`](https://dpwinter.github.io/qsample/sampler.direct.html#directsampler)
and
[`SubsetSampler`](https://dpwinter.github.io/qsample/sampler.subset.html#subsetsampler).
``` python
p_L_low, std_low, p_L_up, std_up = ss_sam.stats()
p_L, std = sam.stats()
sample_range = err_params['q']
plt.errorbar(sample_range, p_L, fmt='--', c="black", yerr=std, label="Direct MC")
plt.loglog(sample_range, p_L_low, label='SS low')
plt.fill_between(sample_range, p_L_low - std_low, p_L_low + std_low, alpha=0.2)
plt.loglog(sample_range, p_L_up, label='SS low')
plt.fill_between(sample_range, p_L_up - std_up, p_L_up + std_up, alpha=0.2)
plt.plot(sample_range, sample_range,'k:', alpha=0.5)
plt.xlabel('$p_{phy}$(q)')
plt.ylabel('$p_L$')
plt.legend();
```
![](index_files/figure-commonmark/cell-12-output-1.png)
## More things to explore
- `qsample.examples` shows more examples of protocol and protocol
samplings.
- `qsample.noise` defines more complex error models, as well as a
superclass
[`ErrorModel`](https://dpwinter.github.io/qsample/noise.html#errormodel)
which can be used to define custom error models.
- `qsample.callbacks` defines more callbacks, as well as the superclass
[`Callback`](https://dpwinter.github.io/qsample/callbacks.html#callback)
which allows for the implementation of custom callbacks.
## Contribute
- Feel free to submit your feature request via github issues
## Team
`qsample` was developed by Don Winter in collaboration with Sascha
Heußen under supervision of Prof. Dr. Markus Müller.
Raw data
{
"_id": null,
"home_page": "https://github.com/dpwinter/qsample/tree/master/",
"name": "qsample",
"maintainer": "",
"docs_url": null,
"requires_python": ">=3.9",
"maintainer_email": "",
"keywords": "qecc,circuit,protocol,sample,sampling",
"author": "Don Winter",
"author_email": "don@wintr.de",
"download_url": "https://files.pythonhosted.org/packages/bb/55/c0d0a6ab89c17dfe294425ec3cd32bebf61a8d23864383fca4f5cae856f8/qsample-0.0.2.tar.gz",
"platform": null,
"description": "qsample\n================\n\n<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->\n\n## Install\n\n pip install qsample\n\n## Prerequisites\n\n- python3 \n- pdflatex (for circuit rendering)\n\n## When to use\n\n- For QEC protocols with in-sequence measurements and feed-forward of\n measurement information\n- Apply circuit-level incoherent Pauli noise at low physical error rates\n (i.e.\u00a0high fidelity physical operations)\n- Simulate and sample protocol execution over ranges of varying physical\n error rates, using customizable callbacks\n\n## Getting started\n\n``` python\nimport qsample as qs # import qsample\nimport matplotlib.pyplot as plt # import matplotlib for visualization of results\n```\n\nFirst, we need to define a quantum protocol of which we would like to\nknow the logical error rate. In `qsample` a protocol is represented as a\ngraph of quantum\n[`Circuit`](https://dpwinter.github.io/qsample/circuit.html#circuit)s as\nnodes and transition `checks` (to be checked at samplingt ime) as edges.\n\nExample: To sample logical error rates of an error-corrected quantum\nstate teleportation protocol, we define the teleportation circuit which\nsends the state of the first to the third qubit.\n\n``` python\nteleport = qs.Circuit([{\"init\": {0, 1, 2}},\n {\"H\": {1}},\n {\"CNOT\": {(1, 2)}},\n {\"CNOT\": {(0, 1)}},\n {\"H\": {0}},\n {\"measure\": {0, 1}}])\n\nteleport.draw()\n```\n\n![](index_files/figure-commonmark/cell-3-output-1.svg)\n\nAdditionally, we need a circuit to (perfectly) measure the third qubit\nafter running `teleport`. If the outcome of this measurement is 0\n(corresponding to the initially prepared $|0\\rangle$ state of qubit 1)\nthe teleportation succeded. If the outcome is 1 however, we want to\ncount a logical failure of this protocol. Let\u2019s create a circuit for\nthis measurement and let\u2019s assume we can perform this measurement\nwithout noise.\n\n``` python\nmeas = qs.Circuit([{\"measure\": {2}}], noisy=False)\n```\n\nBetween the `teleport` and `meas` circuits apply a correction to qubit 3\nconditioned on the measurement outcome (syndrome) of the teleportation\ncircuit. We define the lookup function `lut`\n\n``` python\ndef lut(syn):\n op = {0: 'I', 1: 'X', 2: 'Z', 3: 'Y'}[syn]\n return qs.Circuit([{op: {2}}], noisy=False)\n```\n\nFinally, define the circuit sequence and transition logic together\nwithin a\n[`Protocol`](https://dpwinter.github.io/qsample/protocol.html#protocol)\nobject. Note that protocols must always commence with a unique *START*\nnode and terminate at a unique *FAIL* node, where the latter expresses a\nlogical failure event.\n\n``` python\ntele_proto = qs.Protocol(check_functions={'lut': lut})\ntele_proto.add_nodes_from(['tele', 'meas'], circuits=[teleport, meas])\ntele_proto.add_edge('START', 'tele', check='True')\ntele_proto.add_edge('tele', 'COR', check='lut(tele[-1])')\ntele_proto.add_edge('COR', 'meas', check='True')\ntele_proto.add_edge('meas', 'FAIL', check='meas[-1] == 1')\n\ntele_proto.draw(figsize=(8,5))\n```\n\n![](index_files/figure-commonmark/cell-6-output-1.png)\n\nNotice that we do not define any initial circuit for the correction\n*COR* but pass our lookup function to the `check_functions` dictionary,\nwhich makes it accessible inside the `check` transition statements\n(edges) between circuits. This way we can dynamically insert circuits\ninto the protocol at execution time.\n\nAfter the protocol has been defined we can repeatedly execute\n(i.e.\u00a0sample) it in the presence of incoherent noise. Let\u2019s say we are\ninterested in the logical error rates for physical error rates on all 1-\nand 2-qubit gates of $p_{phy}=10^{-5}, \\dots, 10^{-1}$, and $0.5$. The\ncorresponding noise model is called\n[`E1`](https://dpwinter.github.io/qsample/noise.html#e1) in qsample. The\ngroups of all 1- and 2-qubit gates are indexed by the key *q* in\n[`E1`](https://dpwinter.github.io/qsample/noise.html#e1). Other noise\nmodels (and their parameters) are described in the documentation.\n\n``` python\nerr_model = qs.noise.E1\nerr_params = {'q': [1e-5, 1e-4, 1e-3, 1e-2, 1e-1, 0.5]}\n```\n\nWe are ready to sample. As our protocol only contains Clifford gates\nlet\u2019s choose the\n[`StabilizerSimulator`](https://dpwinter.github.io/qsample/sim.stabilizer.html#stabilizersimulator),\nas well as the\n[`PlotStats`](https://dpwinter.github.io/qsample/callbacks.html#plotstats)\ncallback for plotting the resulting logical error rate as function of\n$p_{phy}$.\n\n``` python\nsam = qs.DirectSampler(protocol=tele_proto, simulator=qs.StabilizerSimulator, err_model=err_model, err_params=err_params)\nsam.run(n_shots=20000, callbacks=[qs.callbacks.PlotStats()])\n```\n\n p=('1.00e-05',): 0%| | 0/20000 [00:00<?, ?it/s]\n\n p=('1.00e-04',): 0%| | 0/20000 [00:00<?, ?it/s]\n\n p=('1.00e-03',): 0%| | 0/20000 [00:00<?, ?it/s]\n\n p=('1.00e-02',): 0%| | 0/20000 [00:00<?, ?it/s]\n\n p=('1.00e-01',): 0%| | 0/20000 [00:00<?, ?it/s]\n\n p=('5.00e-01',): 0%| | 0/20000 [00:00<?, ?it/s]\n\n![](index_files/figure-commonmark/cell-8-output-7.png)\n\nNotice, that at low error rates\n[`DirectSampler`](https://dpwinter.github.io/qsample/sampler.direct.html#directsampler)\nperforms badly, i.e.\u00a0the error bars become very large, as most of the\ntime the protocol is executed error free and, consequently, logical\nerrors are measured infrequently. In this regime it is much more\nefficient to use an importance sampling strategy to avoid fault-free\nprotocol execution and instead put more emphasis on execution with at\nleast one fault happening. This approach is implemented in the\n[`SubsetSampler`](https://dpwinter.github.io/qsample/sampler.subset.html#subsetsampler)\nclass. We only need to specify **two additional parameter** `p_max`\nwhich specifies the $p_{phy}$ at which sampling takes place, and `L`,\nthe length of the longest possible fault-free path. The parameter\n`p_max` must be chosen experimentally by repeated sampling and observing\nwhich subsets have the largest impact on the failure rate. We must\nalways choose a value such that the subset occurence probability has an\nexponentially falling shape. Only in this case are the scaling of the\nsampler results valid. Below we see that for the teleportation circuit\n`p_max`-values of 0.01 and 0.1 are still okay, while 0.3 would be\nproblematic. For more information refer to the linked publication.\n\n``` python\nfor p_phy in [0.01, 0.1, 0.3]:\n Aws = qs.math.subset_probs(teleport, err_model(), p_phy)\n plt.figure()\n plt.title(\"Subset occurence prob. $A_w$ at $p_{phy}$=%.2f\" % p_phy)\n plt.bar(list(map(str,Aws.keys())), Aws.values())\n plt.ylabel(\"$A_w$\")\n plt.xlabel(\"Subsets\")\n```\n\n![](index_files/figure-commonmark/cell-9-output-1.png)\n\n![](index_files/figure-commonmark/cell-9-output-2.png)\n\n![](index_files/figure-commonmark/cell-9-output-3.png)\n\nLet\u2019s choose a $p_{max}=0.1$ for the same error model as before and\nstart sampling. (Note the significant difference in the number of\nsamples).\n\n``` python\nss_sam = qs.SubsetSampler(protocol=tele_proto, simulator=qs.StabilizerSimulator, p_max={'q': 0.1}, err_model=err_model, err_params=err_params, L=3)\nss_sam.run(1000, callbacks=[qs.callbacks.PlotStats()])\n```\n\n p=('1.00e-01',): 0%| | 0/1000 [00:00<?, ?it/s]\n\n![](index_files/figure-commonmark/cell-10-output-2.png)\n\nThe sampling results are internally stored by the\n[`SubsetSampler`](https://dpwinter.github.io/qsample/sampler.subset.html#subsetsampler)\nin a [`Tree`](https://dpwinter.github.io/qsample/sampler.tree.html#tree)\ndata structure. In the tree we can also see why we chose `L`=3, as there\nare three circuits in the fault-free path sequence.\n\n``` python\nss_sam.tree.draw(verbose=True)\n```\n\n![](index_files/figure-commonmark/cell-11-output-1.png)\n\nWe see that only the teleportation protocol has fault weight subsets,\nwhile the *meas* and *COR* circuits are noise-free (i.e.\u00a0they only have\nthe 0-subset). The leaf nodes *FAIL* and *None* represent logical\nfailure and successful teleportation events, respectively. $\\delta$\nrepresents the missing subsets which have not been sampled and which\nresult in the upper bound on the failure rate.\n\nFinally, let\u2019s compare the results of\n[`DirectSampler`](https://dpwinter.github.io/qsample/sampler.direct.html#directsampler)\nand\n[`SubsetSampler`](https://dpwinter.github.io/qsample/sampler.subset.html#subsetsampler).\n\n``` python\np_L_low, std_low, p_L_up, std_up = ss_sam.stats()\np_L, std = sam.stats()\n\nsample_range = err_params['q']\nplt.errorbar(sample_range, p_L, fmt='--', c=\"black\", yerr=std, label=\"Direct MC\")\nplt.loglog(sample_range, p_L_low, label='SS low')\nplt.fill_between(sample_range, p_L_low - std_low, p_L_low + std_low, alpha=0.2)\nplt.loglog(sample_range, p_L_up, label='SS low')\nplt.fill_between(sample_range, p_L_up - std_up, p_L_up + std_up, alpha=0.2)\nplt.plot(sample_range, sample_range,'k:', alpha=0.5)\nplt.xlabel('$p_{phy}$(q)')\nplt.ylabel('$p_L$')\nplt.legend();\n```\n\n![](index_files/figure-commonmark/cell-12-output-1.png)\n\n## More things to explore\n\n- `qsample.examples` shows more examples of protocol and protocol\n samplings.\n- `qsample.noise` defines more complex error models, as well as a\n superclass\n [`ErrorModel`](https://dpwinter.github.io/qsample/noise.html#errormodel)\n which can be used to define custom error models.\n- `qsample.callbacks` defines more callbacks, as well as the superclass\n [`Callback`](https://dpwinter.github.io/qsample/callbacks.html#callback)\n which allows for the implementation of custom callbacks.\n\n## Contribute\n\n- Feel free to submit your feature request via github issues\n\n## Team\n\n`qsample` was developed by Don Winter in collaboration with Sascha\nHeu\u00dfen under supervision of Prof.\u00a0Dr.\u00a0Markus M\u00fcller.\n\n\n",
"bugtrack_url": null,
"license": "Apache Software License 2.0",
"summary": "Efficient sampling of noisy quantum circuits and protocols",
"version": "0.0.2",
"project_urls": {
"Homepage": "https://github.com/dpwinter/qsample/tree/master/"
},
"split_keywords": [
"qecc",
"circuit",
"protocol",
"sample",
"sampling"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "ae258dd8096cad59b98de59737c7ff7bfb9535d86216c5a95e403d06fc005c7e",
"md5": "918e0f2533f6abf593f2fbc2d842f1b6",
"sha256": "b6a85139dec3ab69e49b18b1b2f608814415efd2d07875b278a3644a42db58d2"
},
"downloads": -1,
"filename": "qsample-0.0.2-py3-none-any.whl",
"has_sig": false,
"md5_digest": "918e0f2533f6abf593f2fbc2d842f1b6",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.9",
"size": 44643,
"upload_time": "2023-08-29T13:29:02",
"upload_time_iso_8601": "2023-08-29T13:29:02.972281Z",
"url": "https://files.pythonhosted.org/packages/ae/25/8dd8096cad59b98de59737c7ff7bfb9535d86216c5a95e403d06fc005c7e/qsample-0.0.2-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "bb55c0d0a6ab89c17dfe294425ec3cd32bebf61a8d23864383fca4f5cae856f8",
"md5": "300430df6cc4a99c0ebb4c27c2c3777b",
"sha256": "92be0415d3ef45b574fc57f4eb242f5a0c182a4028029c2b18cc14bb42e33102"
},
"downloads": -1,
"filename": "qsample-0.0.2.tar.gz",
"has_sig": false,
"md5_digest": "300430df6cc4a99c0ebb4c27c2c3777b",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.9",
"size": 43847,
"upload_time": "2023-08-29T13:29:04",
"upload_time_iso_8601": "2023-08-29T13:29:04.814453Z",
"url": "https://files.pythonhosted.org/packages/bb/55/c0d0a6ab89c17dfe294425ec3cd32bebf61a8d23864383fca4f5cae856f8/qsample-0.0.2.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2023-08-29 13:29:04",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "dpwinter",
"github_project": "qsample",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "qsample"
}