Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handling of arrays in templates #37

Open
lneuhaus opened this issue Nov 30, 2020 · 1 comment
Open

Handling of arrays in templates #37

lneuhaus opened this issue Nov 30, 2020 · 1 comment

Comments

@lneuhaus
Copy link

I have two questions / feature request regarding template matching and arrays.

Below there is a code example that uses a blackbird template to extract the gate parameters a user might request in a blackbird program.

Q1: There is a line in the example blow that seems to define a 2-D array:

float array squeezing_amplitudes[1, 4] =
    {squeezing_amplitudes}

I couldn't get it working with a 1-D array, and did not find anything related in the docs. Is there a way to define a 1-D array instead?

float array squeezing_amplitudes[4] =
    {squeezing_amplitudes}

Q2: Q2: While my template defines an array parameter (e.g. squeezing_ampltidues), the template matching returns the individual elements (squeezing_ampltidutes_0_0, ..._0_1, and so on). Is there a trick to get an array or list back from the template matching?

Code example

import blackbird
from blackbird.utils import match_template

prog = blackbird.loads("""
name prog
version 1.0
target X8 (shots=1)
S2gate(0, 0.0) | [0, 4]
S2gate(0, 0.0) | [1, 5]
S2gate(0, 0.0) | [2, 6]
S2gate(0, 0.0) | [3, 7]
MZgate(1.1, 1.1) | [0, 1]
MZgate(1.1, 1.1) | [2, 3]
MZgate(1.1, 1.1) | [1, 2]
MZgate(1.1, 1.1) | [0, 1]
MZgate(1.1, 1.1) | [2, 3]
MZgate(1.1, 1.1) | [1, 2]
MZgate(1.1, 1.1) | [4, 5]
MZgate(1.1, 1.1) | [6, 7]
MZgate(1.1, 1.1) | [5, 6]
MZgate(1.1, 1.1) | [4, 5]
MZgate(1.1, 1.1) | [6, 7]
MZgate(1.1, 1.1) | [5, 6]
Rgate(0.2) | 0
Rgate(0.2) | 1
Rgate(0.2) | 2
Rgate(0.2) | 3
Rgate(0.2) | 4
Rgate(0.2) | 5
Rgate(0.2) | 6
Rgate(0.2) | 7
MeasureFock() | [0, 1, 2, 3, 4, 5, 6, 7]
""")

template = blackbird.loads(
"""
name template
version 1.0
target X8 (shots=1)
float array squeezing_amplitudes[1, 4] =
    {squeezing_amplitudes}
float array phases[1, 12] =
    {phases}
float array final_phases[1, 8] =
    {final_phases}
# for n spatial degrees, first n signal modes, then n idler modes, all phases zero
S2gate(squeezing_amplitudes[0], 0.0) | [0, 4]
S2gate(squeezing_amplitudes[1], 0.0) | [1, 5]
S2gate(squeezing_amplitudes[2], 0.0) | [2, 6]
S2gate(squeezing_amplitudes[3], 0.0) | [3, 7]
# standard 4x4 interferometer for the signal modes (the lower ones in frequency)
# even phase indices correspond to internal Mach-Zehnder interferometer phases
# odd phase indices correspond to external Mach-Zehnder interferometer phases
MZgate(phases[0], phases[1]) | [0, 1]
MZgate(phases[2], phases[3]) | [2, 3]
MZgate(phases[4], phases[5]) | [1, 2]
MZgate(phases[6], phases[7]) | [0, 1]
MZgate(phases[8], phases[9]) | [2, 3]
MZgate(phases[10], phases[11]) | [1, 2]
# duplicate the interferometer for the idler modes (the higher ones in frequency)
MZgate(phases[0], phases[1]) | [4, 5]
MZgate(phases[2], phases[3]) | [6, 7]
MZgate(phases[4], phases[5]) | [5, 6]
MZgate(phases[6], phases[7]) | [4, 5]
MZgate(phases[8], phases[9]) | [6, 7]
MZgate(phases[10], phases[11]) | [5, 6]
# add final dummy phases to allow mapping any unitary to this template (these do not
# affect the photon number measurement)
Rgate(final_phases[0]) | [0]
Rgate(final_phases[1]) | [1]
Rgate(final_phases[2]) | [2]
Rgate(final_phases[3]) | [3]
Rgate(final_phases[4]) | [4]
Rgate(final_phases[5]) | [5]
Rgate(final_phases[6]) | [6]
Rgate(final_phases[7]) | [7]
# measurement in Fock basis
MeasureFock() | [0, 1, 2, 3, 4, 5, 6, 7]
""")

match_template(template, prog)

Output produced by the example

{'squeezing_amplitudes_0_0': 0,
 'phases_0_0': 1.1,
 'phases_0_1': 1.1,
 'phases_0_6': 1.1,
 'phases_0_7': 1.1,
 'final_phases_0_0': 0.2,
 'final_phases_0_4': 0.2,
 'phases_0_4': 1.1,
 ...
@thisac
Copy link
Contributor

thisac commented Mar 1, 2021

Thanks @lneuhaus.

I'll clarify the things we discussed earlier here, so that it's easier to track.

Q1: Due to how arrays are implemented in Blackbird, all arrays could be seen as matrices, i.e. there's only one type (which Blackbird calls "array") which internally works like a 2-dimensional np.ndarray. So there's no way to define a 1-dimensional array as per your example.

Q2: This is also per design, although perhaps not the most intuitive nor effective way. It's done this way due to how free parameters are handled internally. When defining a parameter array, like in your first example in Q1, it gets transformed into an array of many free parameters. This way, internally it works the same as if you would have written out all parameters separately like this:

float array squeezing_amplitudes[1, 1] =
    {squeezing_amplitudes_0_0} {squeezing_amplitudes_0_1} {squeezing_amplitudes_0_2} {squeezing_amplitudes_0_3}

The match_template function then attempts to match these free parameters in the Blackbird template with the corresponding values in the Blackbird script, and thus only sees the ones ending in _0_0, _0_1, etc. This could/should probably be updated so that it instead returns lists with the same shapes as when defined, although this is probably not high on the priorities list right now. Furthermore, even though re-writing Blackbird in a way that's compatible with more advanced logic, as in the above cases, would be nice, I doubt that'll happen soon.

Note: all free parameter names ending in _0_0, _0_1, etc., would currently be reserved for arrays like above. As long as the root of the name differs from a defined array it should be OK, but it's probably best to avoid naming free parameters with this naming scheme.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants