Open gabrielwoolls opened 3 years ago
Yeah tensorflow
is kinda picky about dtype, which is annoying, like here, because:
tensorflow.linalg.svd
returns (complex, real, complex)I'll add an explicit check for tensorflow and complex, unless I think of something else cleaner.
(By the way you might also have to use cutoff=0.0
for autodiff, as none of the compilers/autodiff computational graphs like dynamic shapes.)
Some other issues:
qu.ham_heis(2).astype(DTYPE)
Slightly harder:
tensorflow
doesn't support autodiff through the QR for complex dtypes. One would need to switch all the canonize methods everywhere to use 'svd' rather than 'qr'. In principle that's just changing some method='svd'
kwargs, but I'm not totally sure if it can be controlled all the way from compute_local_expectation
at the moment.I've fixed svd for tensorflow and complex, but the 'tf + QR + complex + autodiff' problem remains.
It may be of interest that the latest (dev) versions of torch
now support complex data - torch generally is more convenient to work with auto-diff wise as it doesn't require (slow and badly scaling) compilation of the computational graph to work pretty efficiently (unlike jax
and tensorflow
).
Ok so one way to skirt around the 'tf + QR + complex + autodiff' problem is override the autoray
lookup of linalg.qr
for tensorflow:
import autoray
def tf_qr(x):
U, s, VH = autoray.do('linalg.svd', x)
dtype = autoray.get_dtype_name(U)
if 'complex' in dtype:
s = autoray.astype(s, dtype)
Q = U
R = autoray.reshape(s, (-1, 1)) * VH
return Q, R
autoray.register_function('tensorflow', 'linalg.qr', tf_qr)
The following now works for me:
import quimb as qu
import quimb.tensor as qtn
from quimb.tensor.optimize import TNOptimizer
LX, LY = 4, 4
DTYPE = 'complex128'
autodiff_backend = 'tensorflow'
autodiff_backend_opts = {'experimental_compile': True}
def state_energy(psi, hterms, vterms, **opts):
he = psi.compute_local_expectation(
hterms, normalized=True, **opts)
ve = psi.compute_local_expectation(
vterms, normalized=True, **opts)
return autoray.do('real', (he + ve))
def normalize_state(psi):
return psi.normalize()
Hij = qu.ham_heis(2).astype(DTYPE)
peps = qtn.PEPS.rand(LX, LY, bond_dim=2, dtype=DTYPE)
hterms = {coos: Hij for coos in peps.gen_horizontal_bond_coos()}
vterms = {coos: Hij for coos in peps.gen_vertical_bond_coos()}
compute_expec_opts = dict(
cutoff=2e-3,
max_bond=9,
contract_optimize='random-greedy')
Hij = qu.ham_heis(2).astype(DTYPE)
peps = qtn.PEPS.rand(LX, LY, bond_dim=2, dtype=DTYPE)
hterms = {coos: Hij for coos in peps.gen_horizontal_bond_coos()}
vterms = {coos: Hij for coos in peps.gen_vertical_bond_coos()}
compute_expec_opts = dict(
cutoff=0.0,
max_bond=9,
contract_optimize='random-greedy')
optmzr = TNOptimizer(
peps,
loss_fn=state_energy,
# norm_fn=normalize_state,
loss_constants={'hterms': hterms,
'vterms': vterms},
loss_kwargs=compute_expec_opts, # these weren't being supplied quite right
autodiff_backend=autodiff_backend,
**autodiff_backend_opts,
)
peps_opt = optmzr.optimize(1000)
Hi @jcmgray, I've been having some trouble using the
TNOptimizer
for complex-valued networks usingtensorflow
backend. I'm attaching an example where optimizing a small PEPS crashes if we try to do boundary MPS contraction.It seems like the
tensor_split
method somehow uses adouble
dtype even if the original PEPS is set to becomplex
, which causes TF to get confused.Is there something simple I'm missing?
This is using the
tensor_2d
branch for thePEPS
stuff of course.