Source code for mimiqcircuits.qcsresults
from mimiqcircuits.proto.qcsrproto import toproto_qcsr, fromproto_qcsr
from mimiqcircuits.proto import qcsresults_pb
from mimiqcircuits.bitstrings import bitvec_to_int
from statistics import mean, median, stdev
[docs]
class QCSResults:
"""
Represents the results of quantum computations obtained from quantum cloud services (QCS).
Args:
simulator (str): The name of the quantum simulator.
version (str): The version of the quantum simulator.
fidelities (list): List of fidelity estimates from different executions.
avggateerrors (list): List of average multi-qubit gate errors from different executions.
cstates (list): List of classical states obtained from executions.
zstates (list): Not used in the current implementation.
amplitudes (dict): Dictionary of statevector amplitudes for different quantum states.
timings (dict): Dictionary of timing information for different phases of the computation.
"""
def __init__(
self,
simulator=None,
version=None,
fidelities=None,
avggateerrors=None,
cstates=None,
zstates=None,
amplitudes=None,
timings=None,
):
self.simulator = simulator
self.version = version
self.fidelities = fidelities if fidelities is not None else []
self.avggateerrors = avggateerrors if avggateerrors is not None else []
self.cstates = cstates if cstates is not None else []
self.zstates = zstates if zstates is not None else []
self.amplitudes = amplitudes if amplitudes is not None else {}
self.timings = timings if timings is not None else {}
def __repr__(self):
result_str = "QCSResults:\n"
if self.simulator is not None:
result_str += f"├── simulator: {self.simulator} {self.version}\n"
# filter out the ones < 1e-7
timings = {k: v for k, v in self.timings.items() if v > 1e-7}
result_str += "├── timings:\n"
for key, value in list(timings.items())[:-1]:
result_str += f"│ ├── {key} time: {value}s\n"
key, value = list(timings.items())[-1]
result_str += f"│ └── {key} time: {value}s\n"
if len(self.fidelities) == 1:
result_str += f"├── fidelity estimate: {self.fidelities[0]:.3g}\n"
result_str += f"├── average multi-qubit gate error estimate: {self.avggateerrors[0]:.3g}\n"
elif len(self.fidelities) != 0:
result_str += "├── fidelity estimate:\n"
result_str += f"│ ├── min, max: {min(self.fidelities):.3g}, {max(self.fidelities):.3g}\n"
result_str += f"│ ├── mean: {mean(self.fidelities):.3g}\n"
result_str += f"│ ├── median: {median(self.fidelities):.3g}\n"
result_str += f"│ └── std: {stdev(self.fidelities):.3g}\n"
result_str += "├── average multi-qubit gate error estimate:\n"
result_str += f"│ ├── min, max: {min(self.avggateerrors):.3g}, {max(self.avggateerrors):.3g}\n"
result_str += f"│ ├── mean: {mean(self.avggateerrors):.3g}\n"
result_str += f"│ ├── median: {median(self.avggateerrors):.3g}\n"
result_str += f"│ └── std: {stdev(self.avggateerrors):.3g}\n"
if len(self.cstates) != 0:
hist = self.histogram()
outcomes = sorted(hist, key=hist.get, reverse=True)[0:5]
result_str += "├── most sampled:\n"
for bs in outcomes[:-1]:
result_str += f'│ ├── bs"{bs.to01()}" => {hist[bs]}\n'
bs = outcomes[-1]
result_str += f'│ └── bs"{bs.to01()}" => {hist[bs]}\n'
result_str += f"├── {len(self.fidelities)} executions\n"
result_str += f"├── {len(self.amplitudes)} amplitudes\n"
result_str += f"└── {len(self.cstates)} samples"
return result_str
def _repr_html_(self):
html_output = "<table><tbody>"
# QCSResults Header
html_output += '<tr><td colspan=2 style="text-align:center;"><strong>QCSResults</strong></td></tr>'
html_output += "<tr><td colspan=2><hr></td></tr>"
# Simulator Information
html_output += '<tr><td colspan=2 style="text-align:center;"><strong>Simulator</strong></td></tr>'
html_output += f'<tr><td colspan=2 style="text-align:center;">{self.simulator} {self.version}</td></tr>'
html_output += "<tr><td colspan=2><hr></td></tr>"
# Timings
html_output += '<tr><td colspan=2 style="text-align:center;"><strong>Timings</strong></td></tr>'
for key, value in self.timings.items():
html_output += f'<tr><td style="text-align:left;">{key} time</td><td>{value}s</td></tr>'
html_output += "<tr><td colspan=2><hr></td></tr>"
# Fidelity Estimates
if self.fidelities:
html_output += "<tr><td colspan=2></td></tr>"
html_output += '<tr><td colspan=2 style="text-align:center;"><strong>Fidelity estimate</strong></td></tr>'
if len(self.fidelities) == 1:
html_output += f'<tr><td style="text-align:left;">Single run value</td><td>{round(self.fidelities[0], 3)}</td></tr>'
else:
html_output += f'<tr><td style="text-align:left;">Mean</td><td>{round(mean(self.fidelities), 3)}</td></tr>'
html_output += f'<tr><td style="text-align:left;">Median</td><td>{round(median(self.fidelities), 3)}</td></tr>'
html_output += f'<tr><td style="text-align:left;">Standard Deviation</td><td>{round(stdev(self.fidelities), 3)}</td></tr>'
html_output += "<tr><td colspan=2><hr></td></tr>"
# Average Multiqubit Error Estimates
if self.avggateerrors:
html_output += "<tr><td colspan=2></td></tr>"
html_output += '<tr><td colspan=2 style="text-align:center;"><strong>Average multiqubit error estimate</strong></td></tr>'
if len(self.avggateerrors) == 1:
html_output += f'<tr><td style="text-align:left;">Single run value</td><td>{round(self.avggateerrors[0], 3)}</td></tr>'
else:
html_output += f'<tr><td style="text-align:left;">Mean</td><td>{round(mean(self.avggateerrors), 3)}</td></tr>'
html_output += f'<tr><td style="text-align:left;">Median</td><td>{round(median(self.avggateerrors), 3)}</td></tr>'
html_output += f'<tr><td style="text-align:left;">Standard Deviation</td><td>{round(stdev(self.avggateerrors), 3)}</td></tr>'
html_output += "<tr><td colspan=2><hr></td></tr>"
# Statistics
html_output += "<tr><td colspan=2></td></tr>"
html_output += '<tr><td colspan=2 style="text-align:center;"><strong>Statistics</strong></td></tr>'
html_output += f'<tr><td style="text-align:left;">Number of executions</td><td>{len(self.fidelities)}</td></tr>'
html_output += f'<tr><td style="text-align:left;">Number of samples</td><td>{len(self.cstates)}</td></tr>'
html_output += f'<tr><td style="text-align:left;">Number of amplitudes</td><td>{len(self.amplitudes)}</td></tr>'
html_output += "<tr><td colspan=2><hr></td></tr>"
# Sampled Classical States
if self.cstates:
html_output += "<tr><td colspan=2></td></tr>"
html_output += '<tr><td colspan=2 style="text-align:center;"><strong>Samples</strong></td></tr>'
hist = self.histogram()
outcomes = sorted(hist, key=hist.get, reverse=True)[0:10]
for bs in outcomes:
html_output += f'<tr><td style="text-align:left;font-family: monospace;">{hex(bitvec_to_int(bs))}</td><td style="text-align:left;font-family: monospace;">{bs.to01()}</td><td style="font-family: monospace;">{hist[bs]}</td></tr>'
html_output += "<tr><td colspan=2><hr></td></tr>"
# Statevector Amplitudes
if self.amplitudes:
html_output += "<tr><td colspan=2></td></tr>"
html_output += '<tr><td colspan=2 style="text-align:center;"><strong>Statevector Amplitudes</strong></td></tr>'
for bs, amp in self.amplitudes.items():
html_output += f'<tr><td style="text-align:left;">{hex(bitvec_to_int(bs.bits))}</td><td style="text-align:left;">{bs.to01()}</td><td>{amp:.3f}</td></tr>'
html_output += "<tr><td colspan=2><hr></td></tr>"
html_output += "</tbody></table>"
return html_output
[docs]
def histogram(self):
"""Histogram of the obtained classical states' occurrences.
Returns:
A dictionary of classical states (bitarray) and their occurrences
(float).
Raises:
TypeError: If a non QCSResults object is passed.
"""
hist = {}
for cstate in self.cstates:
if cstate in hist:
hist[cstate] += 1
else:
hist[cstate] = 1
return hist
[docs]
def saveproto(self, filename):
"""Save QCSResults object to a Protocol Buffers file.
Examples:
>>> from mimiqcircuits import *
>>> from symengine import *
>>> import tempfile
>>> x, y = symbols("x y")
>>> c = Circuit()
>>> c.push(GateH(), 0)
1-qubit circuit with 1 instructions:
└── H @ q[0]
<BLANKLINE>
>>> conn = MimiqConnection()
>>> conn.connect()
Starting authentication server on port 1444 (http://localhost:1444)
>>> job = conn.execute(c)
>>> res = conn.get_results(job)
>>> res
QCSResults:
├── simulator: MIMIQ-StateVector 0.14.1
├── timings:
│ ├── parse time: 7.1008e-05s
│ ├── apply time: 1.3554e-05s
│ ├── total time: 0.00023100600000000002s
│ ├── compression time: 3.207e-06s
│ └── sample time: 4.8047e-05s
├── fidelity estimate: 1
├── average multi-qubit gate error estimate: 0
├── most sampled:
│ ├── bs"0" => 509
│ └── bs"1" => 491
├── 1 executions
├── 0 amplitudes
└── 1000 samples
>>> tmpfile = tempfile.NamedTemporaryFile(suffix=".pb", delete=True)
>>> res.saveproto(tmpfile.name)
9167
>>> res.loadproto(tmpfile.name)
QCSResults:
├── simulator: MIMIQ-StateVector 0.14.1
├── timings:
│ ├── parse time: 7.1008e-05s
│ ├── apply time: 1.3554e-05s
│ ├── total time: 0.00023100600000000002s
│ ├── compression time: 3.207e-06s
│ └── sample time: 4.8047e-05s
├── fidelity estimate: 1
├── average multi-qubit gate error estimate: 0
├── most sampled:
│ ├── bs"0" => 509
│ └── bs"1" => 491
├── 1 executions
├── 0 amplitudes
└── 1000 samples
Note:
This example uses a temporary file to demonstrate the save and load functionality.
You can save your file with any name at any location using:
.. code-block:: python
res.saveproto("example.pb")
res.loadproto("example.pb")
"""
with open(filename, "wb") as f:
return f.write(toproto_qcsr(self).SerializeToString())
[docs]
@staticmethod
def loadproto(filename):
"""Load QCSResults object from a Protocol Buffers file.
The :func:`loadproto` method is a static method and should be called on the class,
not on an instance of the class.
Note:
Look for example in :func:`QCSResults.saveproto`
"""
qcs_results_proto = qcsresults_pb.QCSResults()
with open(filename, "rb") as f:
qcs_results_proto.ParseFromString(f.read())
return fromproto_qcsr(qcs_results_proto)
[docs]
def save_results(filename, results):
"""Saves the results to a ProtoBuf file."""
return results.saveproto(filename)
[docs]
def load_results(filename):
"""Loads QCSResults from a ProtoBuf file."""
return QCSResults.loadproto(filename)
__all__ = ["QCSResults"]