from genQC.imports import *
from genQC.pipeline.diffusion_pipeline import DiffusionPipeline
from genQC.inference.infer_srv import convert_tensors_to_srvs, schmidt_rank_vector
import genQC.platform.qcircuit_dataset_construction as data_const
from genQC.platform.simulation.qcircuit_sim import instruction_name_to_qiskit_gate
import genQC.util as util
from qiskit.quantum_info import DensityMatrix
Editing and masking of circuits
In this notebook we show editing and masking of circuits.
= util.infer_torch_device() # use cuda if we can
device # clean existing memory alloc util.MemoryCleaner.purge_mem()
[INFO]: Cuda device has a capability of 8.6 (>= 8), allowing tf32 matmul.
Setup and load
Load the pre-trained model directly from Hugging Face: Floki00/qc_srv_3to8qubit.
= DiffusionPipeline.from_pretrained("Floki00/qc_srv_3to8qubit", device) pipeline
[INFO]: `genQC.models.unet_qc.QC_Cond_UNet` instantiated from given config on cuda.
[INFO]: `genQC.models.frozen_open_clip.CachedFrozenOpenCLIPEmbedder` instantiated from given config on cuda.
[INFO]: `genQC.models.frozen_open_clip.CachedFrozenOpenCLIPEmbedder`. No save_path` provided. No state dict loaded.
Set 20 sample steps and use rescaled guidance-formula.
= "rescaled"
pipeline.guidance_sample_mode 40)
pipeline.scheduler.set_timesteps(= 7.5 g
1. Editing
Sample a random circuit with desired parameters as the circuit we want to edit:
= [1, 1, 1, 2, 2] # psi_0 state
srv_init = 5 # 5 gates initially placed desired_length
= [instruction_name_to_qiskit_gate(gate) for gate in pipeline.gate_pool]
gate_pool = data_const.get_specific_rnd_srv_circuit(srv_init, desired_length, gate_pool)
init_qc print("SRV is", schmidt_rank_vector(DensityMatrix(init_qc)))
"mpl") init_qc.draw(
SRV is [1, 1, 1, 2, 2]
The editing taks is analogous to image editing, we do img2img with conditioning and copy non-edit areas at every time step. Also called latent_filling
.
def create_edited_circuits(pipeline, samples, qc, prompt, new_length, num_of_qubits, system_size, t_start_index):
#-------------------------------------------
# set mask - appending mask!
= len(qc.data)
old_length
= torch.ones((system_size, new_length), device=device)
qubit_mask = 0
qubit_mask[:, :old_length]
#-------------------------------------------
# prepare and encode
= data_const.gate_pool_to_gate_classes(gate_pool)
gate_classes
= data_const.encode_circuit(qc, system_size, gate_classes, new_length).unsqueeze(0).to(device)
emb_org_image = pipeline.model.embedd_clrs(emb_org_image)
emb_org_image
= emb_org_image.repeat(samples, *[1]*(emb_org_image.dim()-1))
emb_org_images
#-------------------------------------------
# prep condition
= pipeline.text_encoder.tokenize_and_push_to_device(str(prompt))
c = c.repeat(samples, *[1]*(c.dim()-1))
c
#-------------------------------------------
# latent fill
= pipeline.latent_filling(emb_org_images, qubit_mask, c=c, g=g, no_bar=False, t_start_index=t_start_index)
out_tensor = pipeline.model.invert_clr(out_tensor)
out_tensor = out_tensor[:, :num_of_qubits]
out_tensor = torch.unique(out_tensor, dim=0) # we only are interested in unique circuits
out_tensor
= convert_tensors_to_srvs(out_tensor, pipeline.gate_pool, place_barrier=True)
qc_list, error_cnt, srv_list
return qc_list, srv_list
= 16 # how many circuits we sample
samples = 16 # how many gates the model can place
new_length
= [2, 2, 2, 2, 2] # desired target SRV
srv_target
= len(srv_target)
num_of_qubits = t_start_index = int(0.05 * pipeline.scheduler.timesteps.shape[0]) # time step index at which we start denoising
t_start_index
= f"Generate SRV: {srv_target}" # model was trained with this phrase
prompt prompt
'Generate SRV: [2, 2, 2, 2, 2]'
# returns only distinct circuits
= create_edited_circuits(pipeline, samples, init_qc, prompt, new_length, num_of_qubits, num_of_qubits, t_start_index) edited_qc, srv_list
Pick only correct ones:
= []
correct_edited_qc for qc,srv in zip(edited_qc, srv_list):
if srv==srv_target: correct_edited_qc.append(qc)
print(f"We found {len(correct_edited_qc)} correct distinct solutions.")
We found 12 correct distinct solutions.
Compare: initial circuit
"mpl") init_qc.draw(
v.s. edited:
print("SRV is", schmidt_rank_vector(DensityMatrix(correct_edited_qc[0])))
0].draw("mpl", plot_barriers=False) correct_edited_qc[
SRV is [2, 2, 2, 2, 2]
= plt.subplots(2,4, figsize=(18,5), constrained_layout=True)
fig, axs for qc,ax in zip(correct_edited_qc, axs.flatten()):
"mpl", plot_barriers=False, ax=ax)
qc.draw( plt.show()
2. Masking
First we set a desired mask, i.e. a specific layout of a quantum processor.
= 16
max_gates = 5
num_of_qubits
= 3
d #------
def con_set(q1, q2, x, d):
+d] = 1
qubit_mask[q1, x:x+d] = 1
qubit_mask[q2, x:xreturn x+d
#------
= 0
x
= torch.zeros((num_of_qubits, max_gates), device=device) # mask: ones are getting filled, zeros are fixed !
qubit_mask = con_set(0, 1, x, d)
x = con_set(1, 2, x, d)
x = con_set(1, 3, x, d)
x = con_set(3, 4, x, d) x
def plot_mask():
= plt.figure(figsize=(3.7,2), constrained_layout=True)
fig ="Greens")
plt.imshow(qubit_mask.cpu(), cmaprange(0, qubit_mask.shape[1], 2),fontsize=9)
plt.xticks(range(num_of_qubits), fontsize=9)
plt.yticks("Gate sequence / time", fontsize=12)
plt.xlabel("Qubits", fontsize=12)
plt.ylabel(
plt.show() plot_mask()
def get_emb_org_images(pipeline, samples, system_size, max_gates, target_num_gates, target_num_bits, qubit_mask):
= torch.zeros((1, system_size, max_gates), device=device, dtype=torch.int32)
org_image
= len(pipeline.gate_pool) + 1
padd_tok = (torch.ceil(torch.tensor(target_num_gates) / 4) * 4).to(torch.int32)
padd_pos = padd_tok
org_image[:, :, padd_pos:] = padd_tok
org_image[:, target_num_bits:, ]
= pipeline.model.embedd_clrs(org_image)
emb_org_image = emb_org_image.repeat(samples, *[1]*(emb_org_image.dim()-1))
emb_org_images
return emb_org_images
def generate_pattern_SRV(pipeline, prompt, samples, system_size, num_of_qubits, max_gates, qubit_mask, t_start_index=0, target_num_gates=None, target_num_bits=None):
if not exists(target_num_gates):
= max_gates
target_num_gates
if not exists(target_num_bits):
= num_of_qubits
target_num_bits
= get_emb_org_images(pipeline, samples, system_size, max_gates, target_num_gates, target_num_bits, qubit_mask)
emb_org_images
#----------------
# prep condition
= pipeline.text_encoder.tokenize_and_push_to_device(str(prompt))
c = c.repeat(samples, *[1]*(c.dim()-1))
c
#----------------
# latent fill
= pipeline.latent_filling(emb_org_images, qubit_mask, c=c, g=g, no_bar=False, t_start_index=t_start_index)
out_tensor = pipeline.model.invert_clr(out_tensor)
out_tensor = out_tensor[:, :num_of_qubits]
out_tensor = torch.unique(out_tensor, dim=0)
out_tensor
= convert_tensors_to_srvs(out_tensor, pipeline.gate_pool, place_barrier=True)
qc_list, error_cnt, srv_list
return qc_list, srv_list
Now generate circuits corresponding to the mask.
= 512 # how many circuits we sample
samples = [2, 1, 2, 2, 2] # desired target SRV
srv_target
assert len(srv_target)==qubit_mask.shape[0]
= f"Generate SRV: {srv_target}" # model was trained with this phrase
prompt prompt
'Generate SRV: [2, 1, 2, 2, 2]'
= generate_pattern_SRV(pipeline, prompt, samples, num_of_qubits, num_of_qubits, max_gates, qubit_mask, t_start_index=1) qc_list, srv_list
Pick only correct ones:
= []
correct_qc for qc,srv in zip(qc_list, srv_list):
if srv==srv_target: correct_qc.append(qc)
print(f"We found {len(correct_qc)} correct distinct solutions.")
We found 19 correct distinct solutions.
Let’s plot them. Mask:
plot_mask()
v.s. solution:
print("SRV is", schmidt_rank_vector(DensityMatrix(correct_qc[0])))
0].draw("mpl", plot_barriers=False) correct_qc[
SRV is [2, 1, 2, 2, 2]
= plt.subplots(1, min(len(correct_qc), 4), figsize=(18,5), constrained_layout=True)
fig, axs for qc,ax in zip(correct_qc, axs.flatten()):
"mpl", plot_barriers=False, ax=ax)
qc.draw( plt.show()
import genQC
print("genQC Version", genQC.__version__)
genQC Version 0.1.0