高级用法#

MPS 模拟器#

施工中

Very simple, we provide the same set of API for MPSCircuit as Circuit, the only new line is to set the bond dimension for the new simulator.

c = tc.MPSCircuit(n)
c.set_split_rules({"max_singular_values": 50})

The larger bond dimension we set, the better approximation ratio (of course the more computational cost we pay)

分解双量子比特门#

应用在电路上的双量子比特门可以通过 SVD 进行分解,这可以进一步提高收缩路径查找的最优性。

split 配置可以在电路级或门级设置。

split_conf = {
    "max_singular_values": 2,  # how many singular values are kept
    "fixed_choice": 1, # 1 for normal one, 2 for swapped one
}

c = tc.Circuit(nwires, split=split_conf)

# or

c.exp1(
        i,
        (i + 1) % nwires,
        theta=paramc[2 * j, i],
        unitary=tc.gates._zz_matrix,
        split=split_conf
    )

请注意 max_singular_values 必须被指定明以使整个过程成为静态的,因此是可即时编译的。

即时编译函的保存/加载#

要重新使用可即时编译函数,我们可以通过 TensorFlow SavedModel. 的帮助将其保存在磁盘上。也就是说,只有 TensorFlow 后端的可即时编译量子函数才能保存在磁盘上。

对于 jax-backend 量子函数,可以先通过 jax 实验支持将它们转换为 tf-backend 函数: jax2tf

我们将 tf-backend SavedModel 包装为非常易于使用的函数 tensorcircuit.keras.save_func()tensorcircuit.keras.load_func()

参数化测量#

对于 tc.Circuit 上的普通测量 API, 例如 c = tc.Circuit(n=3), 如果我们要评估期望 \(<Z_1Z_2>\), 我们需要调用API为 c.expectation((tc.gates.z(), [1]), (tc.gates.z(), [2]))

在某些情况下,我们可能希望以张量形式告诉软件要测量什么。例如,如果我们想获得上述期望,我们可以使用以下 API : tensorcircuit.templates.measurements.parameterized_measurements()

c = tc.Circuit(3)
z1z2 = tc.templates.measurements.parameterized_measurements(c, tc.array_to_tensor([0, 3, 3, 0]), onehot=True) # 1

此 API 对应于测量 \(I_0Z_1Z_2I_3\), 其中 0、1、2、3 分别用于 I、X、Y、Z 局部运算符。

稀疏矩阵#

我们只支持 COO 格式的稀疏矩阵,因为大多数后端只支持这种格式,下面列出了一些常用的稀疏矩阵后端方法:

def sparse_test():
    m = tc.backend.coo_sparse_matrix(indices=np.array([[0, 1],[1, 0]]), values=np.array([1.0, 1.0]), shape=[2, 2])
    n = tc.backend.convert_to_tensor(np.array([[1.0], [0.0]]))
    print("is sparse: ", tc.backend.is_sparse(m), tc.backend.is_sparse(n))
    print("sparse matmul: ", tc.backend.sparse_dense_matmul(m, n))

for K in ["tensorflow", "jax", "numpy"]:
    with tc.runtime_backend(K):
        print("using backend: ", K)
        sparse_test()

稀疏矩阵对于评估电路上的哈密顿期望特别有用,其中稀疏矩阵表示在空间和时间之间具有良好的效率。请参阅 tensorcircuit.templates.measurements.sparse_expectation() 了解更多详细信息。

对于在张量电路中评估哈密顿期望的不同表示,请参阅 VQE on 1D TFIM with Different Hamiltonian Representation

随机数,即时编译,后端无关特性,和他们的相互作用#

import tensorcircuit as tc
K = tc.set_backend("tensorflow")
K.set_random_state(42)

@K.jit
def r():
    return K.implicit_randn()

print(r(), r()) # different, correct
import tensorcircuit as tc
K = tc.set_backend("jax")
K.set_random_state(42)

@K.jit
def r():
    return K.implicit_randn()

print(r(), r()) # the same, wrong
import tensorcircuit as tc
import jax
K = tc.set_backend("jax")
key = K.set_random_state(42)

@K.jit
def r(key):
    K.set_random_state(key)
    return K.implicit_randn()

key1, key2 = K.random_split(key)

print(r(key1), r(key2)) # different, correct

因此,一个与后端无关并且统一可即时编译的随机基础设施可以表述为

import tensorcircuit as tc
import jax
K = tc.set_backend("tensorflow")

def ba_key(key):
    if tc.backend.name == "tensorflow":
        return None
    if tc.backend.name == "jax":
        return jax.random.PRNGKey(key)
    raise ValueError("unsupported backend %s"%tc.backend.name)


@K.jit
def r(key=None):
    if key is not None:
        K.set_random_state(key)
    return K.implicit_randn()

key = ba_key(42)

key1, key2 = K.random_split(key)

print(r(key1), r(key2))

实现这一目标的更简洁的方法如下:

key = K.get_random_state(42)

@K.jit
def r(key):
    K.set_random_state(key)
    return K.implicit_randn()

key1, key2 = K.random_split(key)

print(r(key1), r(key2))

值得注意的是,由于 Circuit.unitary_krausCircuit.general_kraus 调用 implicit_rand* API,这些 API 的正确用法与上面相同。

有人可能想知道为什么以如此复杂的方式处理随机数,请参阅 Jax 设计说明 提示。

If vmap is also involved apart from jit, I currently find no way to maintain the backend agnosticity as TensorFlow seems to have no support of vmap over random keys (ping me on GitHub if you think you have a way to do this). I strongly recommend the users using Jax backend in the vmap+random setup.