-
Notifications
You must be signed in to change notification settings - Fork 31
/
quantum_ising_chain.py
executable file
·112 lines (84 loc) · 3.76 KB
/
quantum_ising_chain.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# Copyright 2018 PIQuIL - All Rights Reserved
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
import torch
import numpy as np
from qucumber.observables import Observable, to_01, to_pm1
__all__ = ["TFIMChainEnergy"]
class TFIMChainEnergy(Observable):
r"""Observable defining the energy of a Transverse Field Ising Model (TFIM)
spin chain with nearest neighbour interactions, and :math:`J=1`.
:param h: The strength of the tranverse field
:type h: float
:param density: Whether to compute the energy per spin site.
:type density: bool
:param periodic_bcs: If `True` use periodic boundary conditions,
otherwise use open boundary conditions.
:type periodic_bcs: bool
"""
def __init__(self, h):
super(TFIMChainEnergy, self).__init__()
self.h = h
@staticmethod
def _flip_spin(i, s):
s[:, i] *= -1.0
def apply(self, nn_state, samples):
r"""Computes the energy of each sample given a batch of
samples.
:param samples: A batch of samples to calculate the observable on.
Must be using the :math:`\sigma_i = 0, 1` convention.
:type samples: torch.Tensor
"""
samples = to_pm1(samples)
log_psis = -nn_state.rbm_am.effective_energy(to_01(samples)).div(2.)
shape = log_psis.shape + (samples.shape[-1],)
log_flipped_psis = torch.zeros(
*shape, dtype=torch.double, device=nn_state.rbm_am.device
)
for i in range(samples.shape[-1]): # sum over spin sites
self._flip_spin(i, samples) # flip the spin at site i
log_flipped_psis[:, i] = -nn_state.rbm_am.effective_energy(
to_01(samples)
).div(2.)
self._flip_spin(i, samples) # flip it back
log_flipped_psis = torch.logsumexp(log_flipped_psis, 1, keepdim=True).squeeze()
# sum over spin sites
interaction_terms = (samples[:, :-1] * samples[:, 1:]).sum(1)
# convert to ratio of probabilities
transverse_field_terms = log_flipped_psis.sub(log_psis).exp()
energy = transverse_field_terms.mul(self.h).add(interaction_terms).mul(-1.)
return energy.div(samples.shape[-1])
def Convergence(nn_state, tfim_energy, n_measurements, steps):
energy_list = []
err_energy = []
v = torch.bernoulli(
torch.ones(
n_measurements,
nn_state.num_visible,
dtype=torch.double,
device=nn_state.device,
)
* 0.5
)
energy_stats = tfim_energy.statistics_from_samples(nn_state, v)
energy_list.append(energy_stats["mean"])
err_energy.append(energy_stats["std_error"])
for _steps in range(steps):
v = nn_state.sample(1, n_measurements, initial_state=v, overwrite=True)
energy_stats = tfim_energy.statistics_from_samples(nn_state, v)
energy_list.append(energy_stats["mean"])
err_energy.append(energy_stats["std_error"])
out = {"energies": np.array(energy_list), "error": np.array(err_energy)}
return out