qml.MPS offset control

I’m trying to figure out how qml.MPS (qml.MPS — PennyLane 0.29.1 documentation) works. The issue is there is no way to do arbitrary offset between blocks, it’s automatically offsetting block by n_qubits_per_block/2. For example, having a block involving 8 wires, the next one will be 4 qubits lower. And I’m in situation when, for example, i want next block be 6 qubits lower. I couldn’t find any parameters that is responsible for offset. However, It seems the authors of this article(https://arxiv.org/pdf/2209.11058.pdf) somehow managed to do that. Is it possible and if it is, how to control the offset.
Thanks in advance.

Hey @Daniil-Kuhmistrov! I responded to your original Github issue that you made. I’ll close that in favour of discussing your problem here :grin:. Here was my response:

I think I’m a little confused with how you want to use qml.MPS. Are you able to draw a small circuit that outlines how you would like qml.MPS to behave?

image
Sure, @isaacdevlugt , for instance, having a block with n_wires=6, i want the next block to be 4 wires lower instead of 3 that is automatically constructed.

I see! Okay, here’s an example:

import pennylane as qml
import numpy as np


def block(weights, wires):
    qml.RY(weights[0], wires=wires[0])
    qml.RX(weights[1], wires=wires[1])
    qml.RZ(weights[2], wires=wires[2])
    qml.RY(weights[3], wires=wires[3])


n_wires = 6
n_block_wires = 4
n_params_block = 4
n_blocks = qml.MPS.get_n_blocks(range(n_wires), n_block_wires)
print(n_blocks)
template_weights = [[0.1] * n_params_block] * n_blocks

dev = qml.device("default.qubit", wires=range(n_wires))


@qml.qnode(dev)
def circuit(template_weights):
    qml.MPS(range(n_wires), n_block_wires, block, n_params_block, template_weights)
    return qml.expval(qml.PauliZ(wires=n_wires - 1))

print(qml.draw(circuit,expansion_strategy='device')(template_weights))


'''
0: ──RY(0.10)───────────┤     
1: ──RX(0.10)───────────┤     
2: ──RZ(0.10)──RY(0.10)─┤     
3: ──RY(0.10)──RX(0.10)─┤     
4: ──RZ(0.10)───────────┤     
5: ──RY(0.10)───────────┤  <Z>
'''

You’ll notice that the output is printed seemingly “incorrectly” but is actually what you’re after, i.e., I can rewrite it like this:

'''
0: ──RY(0.10)───────────┤     
1: ──RX(0.10)───────────┤     
2: ──RZ(0.10)──RY(0.10)─┤     
3: ──RY(0.10)──RX(0.10)─┤     
4: ────────────RZ(0.10)───────────┤     
5: ────────────RY(0.10)───────────┤  <Z>
'''

The key here is what the argumentsn_wires and n_block_wires mean. If I have a circuit with n_wires and I want 2 blocks of 4 wires (n_block_wires) in the circuit, logically, two of their wires will be shared. Hopefully you can adapt this to fit your needs!

Hi @Daniil-Kuhmistrov ,

Varying the offset is not explicitly supported. But for now you can accomplish this in a messy way by doing the following:

import pennylane as qml

def block(weights, wires):
    qml.MultiControlledX(wires=[wires[i] for i in range(6)])

n_wires = 16
n_block_wires = 8
n_params_block = 2
n_blocks = qml.MPS.get_n_blocks(range(n_wires),n_block_wires)
template_weights = [[0.1,-0.3]]*n_blocks

dev= qml.device('default.qubit',wires=n_wires)
@qml.qnode(dev)
def circuit(template_weights):
    qml.MPS(range(n_wires),n_block_wires,block, n_params_block, template_weights)
    return qml.state()

print(qml.draw(circuit,expansion_strategy='device')(template_weights))

This code should accomplish your goal of having six-wire blocks that only connect via the bottom two blocks. The output is:

 0: ─╭●───────┤  State
 1: ─├●───────┤  State
 2: ─├●───────┤  State
 3: ─├●───────┤  State
 4: ─├●─╭●────┤  State
 5: ─╰X─├●────┤  State
 6: ────├●────┤  State
 7: ────├●────┤  State
 8: ────├●─╭●─┤  State
 9: ────╰X─├●─┤  State
10: ───────├●─┤  State
11: ───────├●─┤  State
12: ───────├●─┤  State
13: ───────╰X─┤  State

Playing around with the number of wires in the block and the number of wires given to the MPS template should allow you to make some offset changes.

I’ll take another look to see if I can come up with a different solution.

2 Likes