Skip to content

Multi qudit

This simulates the operation of a number of connected qudits. Below you can find the API of the simulator.

Config

In this module we define all the configuration parameters for the multiqudit package.

No simulation is performed here. The entire logic is implemented in the spooler.py module.

BarrierInstruction

Bases: BaseModel

The barrier instruction. As each instruction it requires the

Attributes:

Name Type Description
name Literal['barrier']

The string to identify the instruction

wires Annotated[List[Annotated[int, Field(ge=0, le=N_MAX_WIRES - 1)]], Field(min_length=0, max_length=N_MAX_WIRES)]

The wires on which the instruction should be applied so the indices should be between 0 and NUM_WIRES-1

params Annotated[List[float], Field(max_length=0)]

has to be empty

Source code in multiqudit/config.py
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
class BarrierInstruction(BaseModel):
    """
    The barrier instruction. As each instruction it requires the

    Attributes:
        name: The string to identify the instruction
        wires: The wires on which the instruction should be applied
            so the indices should be between 0 and NUM_WIRES-1
        params: has to be empty
    """

    name: Literal["barrier"]
    wires: Annotated[
        List[Annotated[int, Field(ge=0, le=N_MAX_WIRES - 1)]],
        Field(min_length=0, max_length=N_MAX_WIRES),
    ]
    params: Annotated[List[float], Field(max_length=0)]

LoadInstruction

Bases: BaseModel

The load instruction.

Attributes:

Name Type Description
name Literal['load']

How to identify the instruction

wires Annotated[List[Annotated[int, Field(ge=0, le=N_MAX_WIRES - 1)]], Field(min_length=1, max_length=1)]

Exactly one wire has to be given.

params Annotated[List[Annotated[int, Field(ge=1, le=N_MAX_ATOMS)]], Field(min_length=1, max_length=1)]

The number of atoms to be loaded onto the wire.

Source code in multiqudit/config.py
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
class LoadInstruction(BaseModel):
    """
    The load instruction.

    Attributes:
        name: How to identify the instruction
        wires: Exactly one wire has to be given.
        params: The number of atoms to be loaded onto the wire.
    """

    name: Literal["load"]
    wires: Annotated[
        List[Annotated[int, Field(ge=0, le=N_MAX_WIRES - 1)]],
        Field(min_length=1, max_length=1),
    ]
    params: Annotated[
        List[Annotated[int, Field(ge=1, le=N_MAX_ATOMS)]],
        Field(min_length=1, max_length=1),
    ]

LocalSqueezingInstruction

Bases: GateInstruction

The rlz2 instruction. As each instruction it requires the

Attributes:

Name Type Description
name Literal['rlz2']

The string to identify the instruction

wires Annotated[List[Annotated[int, Field(ge=0, le=N_MAX_WIRES - 1)]], Field(min_length=1, max_length=1)]

The wire on which the instruction should be applied so the indices should be between 0 and N_MAX_WIRES-1

params Annotated[List[Annotated[float, Field(ge=0, le=10 * 2 * pi)]], Field(min_length=1, max_length=1)]

has to be empty

Source code in multiqudit/config.py
 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
class LocalSqueezingInstruction(GateInstruction):
    """
    The rlz2 instruction. As each instruction it requires the

    Attributes:
        name: The string to identify the instruction
        wires: The wire on which the instruction should be applied
            so the indices should be between 0 and N_MAX_WIRES-1
        params: has to be empty
    """

    name: Literal["rlz2"] = "rlz2"
    wires: Annotated[
        List[Annotated[int, Field(ge=0, le=N_MAX_WIRES - 1)]],
        Field(min_length=1, max_length=1),
    ]
    params: Annotated[
        List[Annotated[float, Field(ge=0, le=10 * 2 * pi)]],
        Field(min_length=1, max_length=1),
    ]

    # a string that is sent over to the config dict and that is necessary for compatibility with QISKIT.
    parameters: str = "chi"
    description: str = "Evolution under lz2"
    # TODO: This should become most likely a type that is then used for the enforcement of the wires.
    coupling_map: List = [[0], [1], [2], [3], [4]]
    qasm_def: str = "gate rlz2(chi) {}"

MeasureInstruction

Bases: BaseModel

The measure instruction.

Attributes:

Name Type Description
name Literal['measure']

How to identify the instruction

wires Annotated[List[Annotated[int, Field(ge=0, le=N_MAX_WIRES - 1)]], Field(min_length=1, max_length=1)]

Exactly one wire has to be given.

params Annotated[List[float], Field(max_length=0)]

Has to be empty

Source code in multiqudit/config.py
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
class MeasureInstruction(BaseModel):
    """
    The measure instruction.

    Attributes:
        name: How to identify the instruction
        wires: Exactly one wire has to be given.
        params: Has to be empty
    """

    name: Literal["measure"]
    wires: Annotated[
        List[Annotated[int, Field(ge=0, le=N_MAX_WIRES - 1)]],
        Field(min_length=1, max_length=1),
    ]
    params: Annotated[List[float], Field(max_length=0)]

MultiQuditExperiment

Bases: BaseModel

The class that defines the multi qudit experiments

Source code in multiqudit/config.py
261
262
263
264
265
266
267
268
269
270
271
272
273
274
class MultiQuditExperiment(BaseModel):
    """
    The class that defines the multi qudit experiments
    """

    wire_order: Literal["interleaved", "sequential"] = "sequential"

    # mypy keeps throwing errors here because it does not understand the type.
    # not sure how to fix it, so we leave it as is for the moment
    # HINT: Annotated does not work
    shots: Annotated[int, Field(gt=0, le=N_MAX_SHOTS)]
    num_wires: Annotated[int, Field(ge=1, le=N_MAX_WIRES)]
    instructions: List[list]
    seed: Optional[int] = None

MultiQuditFullInstruction

Bases: GateInstruction

The time evolution under the global Hamiltonian. It does not allow for any local control.

Attributes:

Name Type Description
name Literal['multiqudit_full']

The string to identify the instruction

wires Annotated[List[Annotated[int, Field(ge=0, le=N_MAX_WIRES - 1)]], Field(min_length=2, max_length=N_MAX_WIRES)]

The wire on which the instruction should be applied so the indices should be between 0 and N_MAX_WIRES-1

params Annotated[List[Annotated[float, Field(ge=0, le=5000000.0 * pi)]], Field(min_length=5, max_length=5)]

Define the parameter for RX, RZand RZZ on site, RXYon neighboring sites, RZZ on neighboring sites in this order

Source code in multiqudit/config.py
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
class MultiQuditFullInstruction(GateInstruction):
    """
    The time evolution under the global Hamiltonian. It does not allow for any local control.

    Attributes:
        name: The string to identify the instruction
        wires: The wire on which the instruction should be applied
            so the indices should be between 0 and N_MAX_WIRES-1
        params: Define the parameter for `RX`, `RZ`and `RZZ` on site, `RXY`on neighboring sites, `RZZ`
            on neighboring sites in this order
    """

    name: Literal["multiqudit_full"] = "multiqudit_full"
    wires: Annotated[
        List[Annotated[int, Field(ge=0, le=N_MAX_WIRES - 1)]],
        Field(min_length=2, max_length=N_MAX_WIRES),
    ]
    params: Annotated[
        List[Annotated[float, Field(ge=0, le=5e6 * pi)]],
        Field(min_length=5, max_length=5),
    ]

    # a string that is sent over to the config dict and that is necessary for compatibility with QISKIT.
    parameters: str = "omega, delta, chi, Jxy, Jzz"
    description: str = "Apply the Rydberg and Rabi coupling over the whole array."
    # TODO: This should become most likely a type that is then used for the enforcement of the wires.
    coupling_map: List = [[0], [0, 1], [0, 1, 2], [0, 1, 2, 3], [0, 1, 2, 3, 4]]
    qasm_def: str = "gate multiqudit_full(omega, delta, chi, Jxy, Jzz) {}"

MultiQuditSpooler

Bases: Spooler

The class that contains the logic of the multiqudit spooler.

Source code in multiqudit/config.py
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
class MultiQuditSpooler(Spooler):
    """
    The class that contains the logic of the multiqudit spooler.
    """

    def check_dimension(self, json_dict: dict) -> Tuple[str, bool]:
        """
        Make sure that the Hilbert space dimension is not too large.
        """
        dim_ok = False
        err_code = "Wrong experiment name or too many experiments"
        for expr in json_dict:
            num_wires = json_dict[expr]["num_wires"]
            dim_hilbert = 1
            qubit_wires = num_wires
            ins_list = json_dict[expr]["instructions"]
            for ins in ins_list:
                if ins[0] == "load":
                    qubit_wires = qubit_wires - 1
                    dim_hilbert = dim_hilbert * ins[2][0]
            dim_hilbert = dim_hilbert * (2**qubit_wires)
            dim_ok = dim_hilbert < (MAX_HILBERT_SPACE_DIM) + 1
            if not dim_ok:
                err_code = "Hilbert space dimension too large!"
                break
        return err_code.replace("\n", ".."), dim_ok

check_dimension(json_dict)

Make sure that the Hilbert space dimension is not too large.

Source code in multiqudit/config.py
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
def check_dimension(self, json_dict: dict) -> Tuple[str, bool]:
    """
    Make sure that the Hilbert space dimension is not too large.
    """
    dim_ok = False
    err_code = "Wrong experiment name or too many experiments"
    for expr in json_dict:
        num_wires = json_dict[expr]["num_wires"]
        dim_hilbert = 1
        qubit_wires = num_wires
        ins_list = json_dict[expr]["instructions"]
        for ins in ins_list:
            if ins[0] == "load":
                qubit_wires = qubit_wires - 1
                dim_hilbert = dim_hilbert * ins[2][0]
        dim_hilbert = dim_hilbert * (2**qubit_wires)
        dim_ok = dim_hilbert < (MAX_HILBERT_SPACE_DIM) + 1
        if not dim_ok:
            err_code = "Hilbert space dimension too large!"
            break
    return err_code.replace("\n", ".."), dim_ok

RlxInstruction

Bases: GateInstruction

The rlz instruction. As each instruction it requires the

Attributes:

Name Type Description
name Literal['rlx']

The string to identify the instruction

wires Annotated[List[Annotated[int, Field(ge=0, le=N_MAX_WIRES - 1)]], Field(min_length=1, max_length=1)]

The wire on which the instruction should be applied so the indices should be between 0 and N_MAX_WIRES-1

params Annotated[List[Annotated[float, Field(ge=0, le=2 * pi)]], Field(min_length=1, max_length=1)]

has to be empty

Source code in multiqudit/config.py
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
class RlxInstruction(GateInstruction):
    """
    The rlz instruction. As each instruction it requires the

    Attributes:
        name: The string to identify the instruction
        wires: The wire on which the instruction should be applied
            so the indices should be between 0 and N_MAX_WIRES-1
        params: has to be empty
    """

    name: Literal["rlx"] = "rlx"
    wires: Annotated[
        List[Annotated[int, Field(ge=0, le=N_MAX_WIRES - 1)]],
        Field(min_length=1, max_length=1),
    ]
    params: Annotated[
        List[Annotated[float, Field(ge=0, le=2 * pi)]],
        Field(min_length=1, max_length=1),
    ]

    # a string that is sent over to the config dict and that is necessary for compatibility with QISKIT.
    parameters: str = "omega"
    description: str = "Evolution under Lx"
    # TODO: This should become most likely a type that is then used for the enforcement of the wires.
    coupling_map: List = [[0], [1], [2], [3], [4]]
    qasm_def: str = "gate lrx(omega) {}"

RlxlyInstruction

Bases: GateInstruction

The rlxly or rlzlz instruction. As each instruction it requires the

Attributes:

Name Type Description
name Literal['rlxly']

The string to identify the instruction

wires Annotated[List[Annotated[int, Field(ge=0, le=N_MAX_WIRES - 1)]], Field(min_length=2, max_length=N_MAX_WIRES)]

The wire on which the instruction should be applied so the indices should be between 0 and N_MAX_WIRES-1

params Annotated[List[Annotated[float, Field(ge=0, le=10 * 2 * pi)]], Field(min_length=1, max_length=1)]

has to be empty

Source code in multiqudit/config.py
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
class RlxlyInstruction(GateInstruction):
    """
    The rlxly or rlzlz instruction. As each instruction it requires the

    Attributes:
        name: The string to identify the instruction
        wires: The wire on which the instruction should be applied
            so the indices should be between 0 and N_MAX_WIRES-1
        params: has to be empty
    """

    name: Literal["rlxly"] = "rlxly"
    wires: Annotated[
        List[Annotated[int, Field(ge=0, le=N_MAX_WIRES - 1)]],
        Field(min_length=2, max_length=N_MAX_WIRES),
    ]
    params: Annotated[
        List[Annotated[float, Field(ge=0, le=10 * 2 * pi)]],
        Field(min_length=1, max_length=1),
    ]

    # a string that is sent over to the config dict and that is necessary for compatibility with QISKIT.
    parameters: str = "J"
    description: str = "Entanglement between neighboring gates with an xy interaction"
    # TODO: This should become most likely a type that is then used for the enforcement of the wires.
    coupling_map: List = [[0, 1], [1, 2], [2, 3], [3, 4], [0, 1, 2, 3, 4]]
    qasm_def: str = "gate rlylx(J) {}"

RlzInstruction

Bases: GateInstruction

The rlz instruction. As each instruction it requires the

Attributes:

Name Type Description
name Literal['rlz']

The string to identify the instruction

wires Annotated[List[Annotated[int, Field(ge=0, le=N_MAX_WIRES - 1)]], Field(min_length=1, max_length=1)]

The wire on which the instruction should be applied so the indices should be between 0 and N_MAX_WIRES-1

params Annotated[List[Annotated[float, Field(ge=0, le=2 * pi)]], Field(min_length=1, max_length=1)]

has to be empty

Source code in multiqudit/config.py
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
class RlzInstruction(GateInstruction):
    """
    The rlz instruction. As each instruction it requires the

    Attributes:
        name: The string to identify the instruction
        wires: The wire on which the instruction should be applied
            so the indices should be between 0 and N_MAX_WIRES-1
        params: has to be empty
    """

    name: Literal["rlz"] = "rlz"
    wires: Annotated[
        List[Annotated[int, Field(ge=0, le=N_MAX_WIRES - 1)]],
        Field(min_length=1, max_length=1),
    ]
    params: Annotated[
        List[Annotated[float, Field(ge=0, le=2 * pi)]],
        Field(min_length=1, max_length=1),
    ]

    # a string that is sent over to the config dict and that is necessary for compatibility with QISKIT.
    parameters: str = "delta"
    description: str = "Evolution under the Z gate"
    # TODO: This should become most likely a type that is then used for the enforcement of the wires.
    coupling_map: List = [[0], [1], [2], [3], [4]]
    qasm_def: str = "gate rlz(delta) {}"

RlzlzInstruction

Bases: GateInstruction

The rlzlz instruction. As each instruction it requires the

Attributes:

Name Type Description
name Literal['rlzlz']

The string to identify the instruction

wires Annotated[List[Annotated[int, Field(ge=0, le=N_MAX_WIRES - 1)]], Field(min_length=2, max_length=N_MAX_WIRES)]

The wire on which the instruction should be applied so the indices should be between 0 and N_MAX_WIRES-1

params Annotated[List[Annotated[float, Field(ge=0, le=10 * 2 * pi)]], Field(min_length=1, max_length=1)]

has to be empty

Source code in multiqudit/config.py
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
class RlzlzInstruction(GateInstruction):
    """
    The rlzlz instruction. As each instruction it requires the

    Attributes:
        name: The string to identify the instruction
        wires: The wire on which the instruction should be applied
            so the indices should be between 0 and N_MAX_WIRES-1
        params: has to be empty
    """

    name: Literal["rlzlz"] = "rlzlz"
    wires: Annotated[
        List[Annotated[int, Field(ge=0, le=N_MAX_WIRES - 1)]],
        Field(min_length=2, max_length=N_MAX_WIRES),
    ]
    params: Annotated[
        List[Annotated[float, Field(ge=0, le=10 * 2 * pi)]],
        Field(min_length=1, max_length=1),
    ]

    # a string that is sent over to the config dict and that is necessary for compatibility with QISKIT.
    parameters: str = "J"
    description: str = "Entanglement between neighboring gates with a zz interaction"
    # TODO: This should become most likely a type that is then used for the enforcement of the wires.
    coupling_map: List = [[0, 1], [1, 2], [2, 3], [3, 4], [0, 1, 2, 3, 4]]
    qasm_def: str = "gate rlzlz(J) {}"

Simulation code

The module that contains all the necessary logic for the multiqudit.

gen_circuit(exp_name, json_dict)

The function the creates the instructions for the circuit. json_dict: The list of instructions for the specific run.

Source code in multiqudit/spooler.py
 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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
def gen_circuit(exp_name: str, json_dict: ExperimentalInputDict) -> ExperimentDict:
    """The function the creates the instructions for the circuit.
    json_dict: The list of instructions for the specific run.
    """
    # pylint: disable=R0914
    ins_list = json_dict.instructions
    n_shots = json_dict.shots
    if json_dict.seed is not None:
        np.random.seed(json_dict.seed)

    n_wires = json_dict.num_wires
    spin_per_wire = 1 / 2 * np.ones(n_wires)

    for ins in ins_list:
        if ins.name == "load":
            spin_per_wire[ins.wires[0]] = 1 / 2 * ins.params[0]

    dim_per_wire = 2 * spin_per_wire + np.ones(n_wires)
    dim_per_wire = dim_per_wire.astype(int)
    dim_hilbert = np.prod(dim_per_wire)

    # we will need a list of local spin operators as their dimension can change
    # on each wire
    lx_list = []
    ly_list = []
    lz_list = []
    lz2_list = []

    for i1 in np.arange(0, n_wires):
        # let's put together spin matrices
        spin_length = spin_per_wire[i1]
        qudit_range = np.arange(spin_length, -(spin_length + 1), -1)

        lx = csc_matrix(
            1
            / 2
            * diags(
                [
                    np.sqrt(
                        [
                            (spin_length - m + 1) * (spin_length + m)
                            for m in qudit_range[:-1]
                        ]
                    ),
                    np.sqrt(
                        [
                            (spin_length + m + 1) * (spin_length - m)
                            for m in qudit_range[1:]
                        ]
                    ),
                ],
                [-1, 1],
            )
        )
        ly = csc_matrix(
            1
            / (2 * 1j)
            * diags(
                [
                    np.sqrt(
                        [
                            (spin_length - m + 1) * (spin_length + m)
                            for m in qudit_range[:-1]
                        ]
                    ),
                    -1
                    * np.sqrt(
                        [
                            (spin_length + m + 1) * (spin_length - m)
                            for m in qudit_range[1:]
                        ]
                    ),
                ],
                [-1, 1],
            )
        )
        lz = csc_matrix(diags([qudit_range], [0]))
        lz2 = lz.dot(lz)

        lx_list.append(op_at_wire(lx, i1, list(dim_per_wire)))
        ly_list.append(op_at_wire(ly, i1, list(dim_per_wire)))
        lz_list.append(op_at_wire(lz, i1, list(dim_per_wire)))
        lz2_list.append(op_at_wire(lz2, i1, list(dim_per_wire)))

    initial_state = 1j * np.zeros(dim_per_wire[0])
    initial_state[0] = 1 + 1j * 0
    psi = sparse.csc_matrix(initial_state)
    for i1 in np.arange(1, len(dim_per_wire)):
        initial_state = 1j * np.zeros(dim_per_wire[i1])
        initial_state[0] = 1 + 1j * 0
        psi = sparse.kron(psi, initial_state)
    psi = psi.T

    measurement_indices = []
    shots_array = []
    for inst in ins_list:
        if inst.name == "rlx":
            position = inst.wires[0]
            theta = inst.params[0]
            psi = expm_multiply(-1j * theta * lx_list[position], psi)
        if inst.name == "rly":
            position = inst.wires[0]
            theta = inst.params[0]
            psi = expm_multiply(-1j * theta * ly_list[position], psi)
        if inst.name == "rlz":
            position = inst.wires[0]
            theta = inst.params[0]
            psi = expm_multiply(-1j * theta * lz_list[position], psi)
        if inst.name == "rlz2":
            position = inst.wires[0]
            theta = inst.params[0]
            psi = expm_multiply(-1j * theta * lz2_list[position], psi)
        if inst.name == "rlxly":
            # apply gate on two qudits
            if len(inst.wires) == 2:
                position1 = inst.wires[0]
                position2 = inst.wires[1]
                theta = inst.params[0]
                lp1 = lx_list[position1] + 1j * ly_list[position1]
                lp2 = lx_list[position2] + 1j * ly_list[position2]
                lxly = lp1.dot(lp2.conjugate().T)
                lxly = lxly + lxly.conjugate().T
                psi = expm_multiply(-1j * theta * lxly, psi)
            # apply gate on all qudits
            elif len(inst.wires) == n_wires:
                theta = inst.params[0]
                lxly = csc_matrix((dim_hilbert, dim_hilbert))
                for i1 in np.arange(0, n_wires - 1):
                    lp1 = lx_list[i1] + 1j * ly_list[i1]
                    lp2 = lx_list[i1 + 1] + 1j * ly_list[i1 + 1]
                    lxly = lxly + lp1.dot(lp2.conjugate().T)
                lxly = lxly + lxly.conjugate().T
                psi = expm_multiply(-1j * theta * lxly, psi)
        if inst.name == "rlzlz":
            # apply gate on two quadits
            if len(inst.wires) == 2:
                position1 = inst.wires[0]
                position2 = inst.wires[1]
                theta = inst.params[0]
                lzlz = lz_list[position1].dot(lz_list[position2])
                psi = expm_multiply(-1j * theta * lzlz, psi)
        if inst.name == "multiqudit_full":
            omega, delta, chi, jxy, jzz = inst.params
            u_full = csc_matrix((dim_hilbert, dim_hilbert))
            # first the RX
            for lxi in lx_list:
                u_full = u_full + omega * lxi
            # next the RZ
            for lzi in lz_list:
                u_full = u_full + delta * lzi
            # next the local squeezing
            for lz2i in lz2_list:
                u_full = u_full + chi * lz2i
            # next the neighboring flip-flop
            for ii in range(n_wires - 1):
                position1 = ii
                position2 = ii + 1
                lp1 = lx_list[position1] + 1j * ly_list[position1]
                lp2 = lx_list[position2] + 1j * ly_list[position2]
                lxly = lp1.dot(lp2.conjugate().T)
                lxly = lxly + lxly.conjugate().T
                u_full = u_full + jxy * lxly
            # next the neighboring zz
            for ii in range(n_wires - 1):
                lzlz = lz_list[ii].dot(lz_list[ii + 1])
                u_full = u_full + jzz * lzlz
            psi = expm_multiply(-1j * u_full, psi)
        if inst.name == "measure":
            measurement_indices.append(inst.wires[0])
    if measurement_indices:
        # the following filters out the results for the indices we prefer.
        probs = np.squeeze(abs(psi.toarray()) ** 2)
        result_ind = np.random.choice(dim_hilbert, p=probs, size=n_shots)
        measurements = np.zeros((n_shots, len(measurement_indices)), dtype=int)
        for i1 in range(n_shots):
            observed = np.unravel_index(result_ind[i1], dim_per_wire)
            # TODO these types are messed up for the moment
            # as ususal we add an ignore until this gets back to bite us in the ...
            # but it simply to tough to find out where the typing goes wrong right now.
            observed = np.array(observed)  # type: ignore
            measurements[i1, :] = observed[measurement_indices]  # type: ignore
        shots_array = measurements.tolist()

    exp_sub_dict = create_memory_data(shots_array, exp_name, n_shots, ins_list)
    return exp_sub_dict

op_at_wire(op, pos, dim_per_wire)

Applies an operation onto the wire and provides unitaries on the other wires. Basically this creates the nice tensor products.

Parameters:

Name Type Description Default
op matrix

The operation that should be applied.

required
pos int

The wire onto which the operation should be applied.

required
dim_per_wire int

What is the local Hilbert space of each wire.

required

Returns:

Type Description
csc_matrix

The tensor product matrix.

Source code in multiqudit/spooler.py
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
def op_at_wire(op: csc_matrix, pos: int, dim_per_wire: List[int]) -> csc_matrix:
    """
    Applies an operation onto the wire and provides unitaries on the other wires.
    Basically this creates the nice tensor products.

    Args:
        op (matrix): The operation that should be applied.
        pos (int): The wire onto which the operation should be applied.
        dim_per_wire (int): What is the local Hilbert space of each wire.

    Returns:
        The tensor product matrix.
    """
    # There are two cases the first wire can be the identity or not
    if pos == 0:
        res = op
    else:
        res = csc_matrix(identity(dim_per_wire[0]))
    # then loop for the rest
    for i1 in np.arange(1, len(dim_per_wire)):
        temp = csc_matrix(identity(dim_per_wire[i1]))
        if i1 == pos:
            temp = op
        res = sparse.kron(res, temp)

    return res

Comments