New features and important updates
1.Variational quantum circuits support structural reuse as well as the use of expressions as gate parameters for parameterized gates.
def get_vqc1():
vqc = VQCircuit()
vqc.set_Param([2])
p0 = vqc.Param([0], 'p0')
p1 = vqc.Param([1], 'p1')
e = 3.14 * p0 * p1 + p1 + 4
vqc << X(0)
vqc << RX(0, 3.14)
vqc << RY(0, e)
return vqc
def get_vqc2():
vqc2 = VQCircuit()
vqc2.set_Param([3])
P0 = vqc2.Param([0], 'P0')
P1 = vqc2.Param([1], 'P1')
P2 = vqc2.Param([2], 'P2')
cir = QCircuit()
cir << Y(0)
vqc2 << cir
vqc1 = get_vqc1()
vqc2.append(vqc1, [(vqc1.Param([0]), vqc2.Param([0])), (vqc1.Param([1]), vqc2.Param([1]))])
return vqc2
vqc1 = get_vqc1()
print('vqc1:')
vqc1.display_ansatz()
print()
vqc2 = get_vqc2()
print('vqc2:')
vqc2.display_ansatz()
Output result of the example code
vqc1:
X q[0]
RX q[0],(3.14000000)
RY q[0],(((3.14*p0*p1+p1)+4))
vqc2:
Y q[0]
X q[0]
RX q[0],(3.14000000)
RY q[0],(((3.14*P0*P1+P1)+4))
2.Variational quantum circuits support gradient calculation and provide an interface to obtain both the expectation value and the gradient value simultaneously.
def get_vqc():
vqc = VQCircuit()
vqc.set_Param([2])
vqc << RX(0, vqc.Param([0]))
vqc << RY(1, vqc.Param([1]))
return vqc
def get_param_val():
param_val = [2.14, 3.14]
return param_val
def get_hamiltonian():
paulis = [
('YY', [0, 1], (12.36525580995888 + 14.85172018664403j)),
('YX', [0, 1], (12.920260765526914 + 26.29613065236354j))
]
return Hamiltonian(paulis)
def get_gradient(vqc, param_val, ham):
return vqc.get_gradients(params=param_val, observable=ham, diff_method=DiffMethod.ADJOINT_DIFF)
def get_gradient2(vqc: VQCircuit, param_val, ham):
return vqc.get_gradients_and_expectation(params=param_val, observable=ham, diff_method=DiffMethod.ADJOINT_DIFF)
vqc = get_vqc()
param_val = get_param_val()
ham = get_hamiltonian()
res1 = get_gradient(vqc, param_val, ham)
res2 = get_gradient2(vqc, param_val, ham)
print('res1:',res1)
print('res1 gradient:',res1.gradients())
print('res2:',res2)
print('res2 gradient:',res2.gradients())
Output result of the example code
res1: {gradient(double):{Parameter.at([0]):0.0110905,Parameter.at([1]):23.3932}}
res1 gradient: [0.011090474368968659, 23.39317090007428]
res2: (gradient and expectation){"expectation":-0.017333,"gradients":{Parameter.at([0]):0.0110905,Parameter.at([1]):23.3932}}
res2 gradient: [0.011090474368968659, 23.39317090007428]
3.Add support for SQISWAP, ECHO, IDLE, CRX, CRY, and CRZ gates
ir_str="""
QINIT 2
CREG 1
CRX q[0],q[1],(3.14000000)
CRY q[0],q[1],(4.14000000)
CRZ q[0],q[1],(5.14000000)
ECHO q[0]
SQISWAP q[0],q[1]
IDLE q[0],(3.66666006000)
"""
prog = QProg(ir_str)
ir_2 = prog.originir(8)
print('ir_2:\n',ir_2)
prog3 = QProg()
prog3 << CRX(0,1,30.14)
prog3 << CRY(0,1,40.14)
prog3 << CRZ(0,1,50.14)
prog3 << ECHO(0)
prog3 << SQISWAP(0,1)
prog3 << IDLE(0,30.66666)
ir_3 = prog3.originir(8)
print('ir_3:\n',ir_3)
vqc = VQCircuit()
vqc.set_Param([0])
vqc << CRX(0,1,30.14)
vqc << CRY(0,1,40.14)
vqc << CRZ(0,1,50.14)
vqc << ECHO(0)
vqc << SQISWAP(0,1)
vqc << IDLE(0,30.66666)
cir = vqc([]).at([0])
print('cir ir:\n',cir.originir(8))
Output result of the example code
ir_2:
QINIT 2
CREG 1
CONTROL q[0]
RX q[1],(3.14000000)
ENDCONTROL
CONTROL q[0]
RY q[1],(4.14000000)
ENDCONTROL
CONTROL q[0]
RZ q[1],(5.14000000)
ENDCONTROL
ECHO q[0]
SQISWAP q[0],q[1]
IDLE q[0],(3.00000000)
ir_3:
QINIT 2
CREG 1
CONTROL q[0]
RX q[1],(30.14000000)
ENDCONTROL
CONTROL q[0]
RY q[1],(40.14000000)
ENDCONTROL
CONTROL q[0]
RZ q[1],(50.14000000)
ENDCONTROL
ECHO q[0]
SQISWAP q[0],q[1]
IDLE q[0],(30.00000000)
cir ir:
QINIT 2
CREG 1
CONTROL q[0]
RX q[1],(30.14000000)
ENDCONTROL
CONTROL q[0]
RY q[1],(40.14000000)
ENDCONTROL
CONTROL q[0]
RZ q[1],(50.14000000)
ENDCONTROL
ECHO q[0]
SQISWAP q[0],q[1]
IDLE q[0],(30.00000000)
4.Added the ChipBackend
class, which stores the information used by the chip backend for compilation, and added the transpile
interface, which compiles a quantum program to a specified chip backend by passing in the ChipBackend
parameter. Additionally, QCloud has added the functionality to obtain the chip backend from the cloud platform.
Example usage is as follows:
import time
apikey = ''
def test_backend():
print("test_get_topo")
service = QCloudService(apikey)
backends = service.backends()
for backend in backends:
print(backend)
backend_wukong = service.backend("72")
backend = backend_wukong.chip_info().get_chip_backend()
all_qbits = list(range(10))
circuit = random_qcircuit(all_qbits, 100, ["RX", "RY", "RZ", "H", "U1", "U2", "U3", "CP", "CNOT", "SWAP"])
prog_1 = QProg()
prog_1 << circuit
for i in all_qbits:
prog_1 << measure(i, i)
transpiler = Transpiler()
print("Enter transpile")
prog_2 = transpiler.transpile(prog_1, backend, {}, 2)
if __name__ == "__main__":
test_backend()
- Added the interface for exporting quantum chip instructions in
QProg
. Quantum chip instructions are files that can be directly sent to the chip for execution. Currently, the instruction format of this interface only supports the original quantum computer. Using this interface requires that the quantum program already consists entirely of the chip's basic instructions (RPhi, CZ, Measure, and ECHO). to_instruction
has three parameters. The first parameter is the specified chip backend. The second parameter is the offset of the qubit during the instruction conversion process. The third parameter is whether to use pattern to layer the instructions.
This interface can be used in conjunction with to_instruction
and run_instruction
to compile a quantum program to a specified chip on the cloud platform, convert it into an instruction file, and run it on the chip. Here is an example code for the complete process:
api_key = ""
def run_instruction_test(prog_1: QProg):
service = QCloudService(api_key)
backend_wukong = service.backend("72")
chip_info = backend_wukong.chip_info()
backend: ChipBackend = chip_info.get_chip_backend()
transpiler = Transpiler()
prog_2 = transpiler.transpile(prog_1, backend, {}, 2)
use_pattern = False
ins = prog_2.to_instruction(backend, 1, use_pattern)
options = QCloudOptions()
options.set_mapping(False)
options.set_amend(False)
job = backend_wukong.run_instruction([ins], 1000, options)
result = job.result()
print(result.get_probs_list())
if __name__ == "__main__":
ir = """
QINIT 8
CREG 8
H q[0]
H q[1]
H q[2]
H q[3]
H q[4]
H q[5]
CNOT q[0],q[4]
RZ q[4],(0.93926449)
CNOT q[0],q[4]
CNOT q[0],q[5]
RZ q[5],(0.42459902)
CNOT q[0],q[5]
CNOT q[1],q[4]
RZ q[4],(0.88779795)
CNOT q[1],q[4]
CNOT q[1],q[5]
RZ q[5],(0.46319893)
CNOT q[1],q[5]
CNOT q[2],q[5]
RZ q[5],(1.132264)
CNOT q[2],q[5]
CNOT q[3],q[5]
RZ q[5],(0.86206467)
CNOT q[3],q[5]
RX q[0],(2.3543679)
RX q[1],(2.3543679)
RX q[2],(2.3543679)
RX q[3],(2.3543679)
RX q[4],(2.3543679)
RX q[5],(2.3543679)
MEASURE q[0],c[0]
MEASURE q[1],c[1]
MEASURE q[2],c[2]
MEASURE q[3],c[3]
MEASURE q[4],c[4]
MEASURE q[5],c[5]
"""
prog_1 = convert_originir_string_to_qprog(ir)
run_instruction_test(prog_1)
The execution result is as follows:
[{'0x0': 0.077, '0x1': 0.007, '0x10': 0.032, '0x11': 0.005, '0x12': 0.043, '0x13': 0.006,
'0x14': 0.009, '0x15': 0.002, '0x16': 0.018, '0x17': 0.005, '0x18': 0.022, '0x19': 0.002,
'0x1a': 0.018, '0x1b': 0.005, '0x1c': 0.018, '0x1d': 0.002, '0x1e': 0.025, '0x1f': 0.003,
'0x2': 0.084, '0x20': 0.036, '0x21': 0.004, '0x22': 0.039, '0x23': 0.003, '0x24': 0.013,
'0x26': 0.011, '0x27': 0.006, '0x28': 0.03, '0x29': 0.004, '0x2a': 0.024, '0x2b': 0.002,
'0x2c': 0.016, '0x2d': 0.006, '0x2e': 0.029, '0x2f': 0.006, '0x3': 0.018, '0x30': 0.014,
'0x31': 0.003, '0x32': 0.019, '0x33': 0.005, '0x34': 0.006, '0x35': 0.002, '0x36': 0.009,
'0x37': 0.002, '0x38': 0.009, '0x39': 0.002, '0x3a': 0.012, '0x3b': 0.001, '0x3c': 0.008,
'0x3d': 0.001, '0x3e': 0.016, '0x3f': 0.001, '0x4': 0.023, '0x5': 0.002, '0x6': 0.03,
'0x7': 0.012, '0x8': 0.042, '0x9': 0.008, '0xa': 0.042, '0xb': 0.004, '0xc': 0.038,
'0xd': 0.005, '0xe': 0.046, '0xf': 0.008}]
5.Quantum cloud services add instruction set tasks, which can compile quantum programs into instruction sets supported by the specified backend and then directly run the instruction set tasks.
prog = QProg()
prog << CZ(9, 10)
prog << CZ(10, 11)
prog.append(measure(9,9))
online_key = "api_key"
service = QCloudService(online_key)
service.setup_logging()
backend = service.backend("72")
options = QCloudOptions()
options.set_amend(False)
ins = prog.to_instruction(backend.chip_backend())
job = backend.run_instruction([ins,ins], 1000, options)
chip_info = backend.chip_info()
result = job.result()
print(result.get_probs_list())
The output is:
[{'0x0': 0.7249999761581421, '0x1': 0.2749999940395355}, {'0x0': 0.75, '0x1': 0.25}]
Fixed bug
1.Fix the occasional crash issue that occurs during OrginIR conversion in multi-threaded programs.
2.Fix the error that occurs when SIMD optimization is triggered for multi-controlled gates with more than 14 qubits.
3.Optimize the decomposition algorithm for multi-controlled gates to improve efficiency.
4.Enhance the performance of the transpile interface.