/
freeformsim.py
157 lines (129 loc) · 5.99 KB
/
freeformsim.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
"""
ModelTest Protocol objects
"""
#***************************************************************************************************
# Copyright 2015, 2019 National Technology & Engineering Solutions of Sandia, LLC (NTESS).
# Under the terms of Contract DE-NA0003525 with NTESS, the U.S. Government retains certain rights
# in this software.
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
# in compliance with the License. You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0 or in the LICENSE file in the root pyGSTi directory.
#***************************************************************************************************
import numpy as _np
from pygsti.protocols import protocol as _proto
from pygsti.circuits.circuit import Circuit as _Circuit
from pygsti.data.freedataset import FreeformDataSet as _FreeformDataSet
from pygsti.modelmembers import states as _state
class FreeformDataSimulator(_proto.DataSimulator):
"""
Computes arbitrary functions of the state data simulator that also computes user-defined functions
of the final states.
"""
def __init__(self):
super().__init__()
def compute_freeform_data(self, circuit):
"""
Computes the simulated free-form data for a single circuit.
Parameters
----------
circuit : Circuit
The circuit to compute data for.
Returns
-------
dict
"""
raise NotImplementedError("Derived classes should implement this!")
def run(self, edesign, memlimit=None, comm=None):
"""
Run this data simulator on an experiment design.
Parameters
----------
edesign : ExperimentDesign
The input experiment design.
memlimit : int, optional
A rough per-processor memory limit in bytes.
comm : mpi4py.MPI.Comm, optional
When not ``None``, an MPI communicator used to run this data
simulator in parallel.
Returns
-------
ProtocolData
"""
dataset = _FreeformDataSet(circuits=edesign.all_circuits_needing_data)
for c in edesign.all_circuits_needing_data:
dataset[c] = self.compute_freeform_data(c)
return _proto.ProtocolData(edesign, dataset)
def apply_fn(self, series):
import pandas as _pd
circuit = _Circuit.cast(series['Circuit']) # parse string circuit
info = self.compute_freeform_data(circuit)
return _pd.Series(info) # TODO FIX THIS
def apply(self, df):
"""
Apply this data simulator to a data frame having a `Circuit` column.
Parameters
----------
df : pandas.DataFrame
The data frame to apply to.
Returns
-------
pandas.DataFrame
"""
import pandas as _pd
return _pd.concat([df, df.apply(self.apply_fn, axis=1)], axis=1)
class ModelFreeformSimulator(FreeformDataSimulator):
"""
A base class for data simulators that utilize models (probably most of them!).
Holds a dictionary of models and provides basic functionality for computing
probabilities, final states, and process matrices corresponding to circuits
which make implementing :meth:`compute_freeform_data` easier.
Parameters
----------
models : dict
A dictionary whose keys are string labels and values are :class:`Model` objects, specifying
the models used to compute "simluated" data.
"""
def __init__(self, models):
self.models = models
def compute_process_matrix(self, model, circuit, include_final_state=False, include_probabilities=False):
prep, circuit_ops, povm = model.split_circuit(circuit)
mx = model.sim.product(circuit_ops)
if include_final_state or include_probabilities:
ret = [mx]
rho = model.circuit_layer_operator(prep, 'prep')
final_state = _state.StaticState(_np.dot(mx, rho.to_dense(on_space='HilbertSchmidt')),
model.basis, model.evotype, model.state_space)
if include_final_state:
ret.append(final_state)
if include_probabilities:
M = model.circuit_layer_operator(povm, 'povm')
probs = M.acton(final_state)
ret.append(probs)
return tuple(ret)
else:
return mx
def compute_process_matrices(self, circuit, include_final_state=False, include_probabilities=False):
return {model_lbl: self.compute_process_matrix(model, circuit, include_final_state, include_probabilities)
for model_lbl, model in self.models.items()}
def compute_final_state(self, model, circuit, include_probabilities=False):
complete_circuit = model.complete_circuit(circuit).layertup
rho = model.circuit_layer_operator(complete_circuit[0], 'prep')
for layer in complete_circuit[1:-1]:
layerop = model.circuit_layer_operator(layer, 'op')
rho = layerop.acton(rho)
if include_probabilities:
M = model.circuit_layer_operator(complete_circuit[-1], 'povm')
probs = M.acton(rho)
return rho, probs
return rho
def compute_final_states(self, circuit, include_probabilities=False):
return {model_lbl: self.compute_final_state(model, circuit, include_probabilities)
for model_lbl, model in self.models.items()}
def compute_circuit_probabilities(self, model, circuit):
# FUTURE: add a flag in __init__ (?) for computing bulk probabilities at the beginning of
# run(...) (we'll need to overload run for this) and then this function just indexes the
# precomputed values.
return model.probabilities(circuit)
def compute_probabilities(self, circuit, include_probabilities=False):
return {model_lbl: self.compute_circuit_probabilities(model, circuit)
for model_lbl, model in self.models.items()}