Skip to content
This repository was archived by the owner on Nov 7, 2024. It is now read-only.

Commit 7404ed8

Browse files
authored
Backend broadcast 2 (#514)
* adding broadcast_right_multiplication to backends * fix test * bradcasting support added
1 parent f5a53c5 commit 7404ed8

12 files changed

Lines changed: 204 additions & 13 deletions

tensornetwork/backends/base_backend.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -475,7 +475,9 @@ def inv(self, matrix: Tensor) -> Tensor:
475475
def broadcast_right_multiplication(self, tensor1: Tensor, tensor2: Tensor):
476476
"""
477477
Perform broadcasting for multiplication of `tensor2` onto `tensor1`, i.e.
478-
`tensor1` * tensor2`.
478+
`tensor1` * tensor2`, where `tensor1` is an arbitrary tensor and `tensor2` is a
479+
one-dimensional tensor. The broadcasting is applied to the last index of
480+
`tensor1`.
479481
Args:
480482
tensor1: A tensor.
481483
tensor2: A tensor.
@@ -485,3 +487,19 @@ def broadcast_right_multiplication(self, tensor1: Tensor, tensor2: Tensor):
485487
raise NotImplementedError(
486488
"Backend '{}' has not implemented `broadcast_right_multiplication`."
487489
.format(self.name))
490+
491+
def broadcast_left_multiplication(self, tensor1: Tensor, tensor2: Tensor):
492+
"""
493+
Perform broadcasting for multiplication of `tensor1` onto `tensor2`, i.e.
494+
`tensor1` * tensor2`, where `tensor2` is an arbitrary tensor and `tensor1` is a
495+
one-dimensional tensor. The broadcasting is applied to the first index of
496+
`tensor2`.
497+
Args:
498+
tensor1: A tensor.
499+
tensor2: A tensor.
500+
Returns:
501+
Tensor: The result of multiplying `tensor1` onto `tensor2`.
502+
"""
503+
raise NotImplementedError(
504+
"Backend '{}' has not implemented `broadcast_left_multiplication`."
505+
.format(self.name))

tensornetwork/backends/jax/jax_backend_test.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,3 +313,21 @@ def test_broadcast_right_multiplication_raises():
313313
tensor2 = backend.randn((3, 3))
314314
with pytest.raises(ValueError):
315315
backend.broadcast_right_multiplication(tensor1, tensor2)
316+
317+
318+
@pytest.mark.parametrize("dtype", [np.float64, np.complex128])
319+
def test_broadcast_left_multiplication(dtype):
320+
backend = jax_backend.JaxBackend()
321+
tensor1 = backend.randn((3,), dtype=dtype, seed=10)
322+
tensor2 = backend.randn((3, 4, 2), dtype=dtype, seed=10)
323+
out = backend.broadcast_left_multiplication(tensor1, tensor2)
324+
np.testing.assert_allclose(out, np.reshape(tensor1, (3, 1, 1)) * tensor2)
325+
326+
327+
def test_broadcast_left_multiplication_raises():
328+
dtype = np.float64
329+
backend = jax_backend.JaxBackend()
330+
tensor1 = backend.randn((3, 3), dtype=dtype, seed=10)
331+
tensor2 = backend.randn((2, 4, 3), dtype=dtype, seed=10)
332+
with pytest.raises(ValueError):
333+
backend.broadcast_left_multiplication(tensor1, tensor2)

tensornetwork/backends/numpy/numpy_backend.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,3 +405,12 @@ def broadcast_right_multiplication(self, tensor1: Tensor, tensor2: Tensor):
405405
raise ValueError("only order-1 tensors are allowed for `tensor2`,"
406406
" found `tensor2.shape = {}`".format(tensor2.shape))
407407
return tensor1 * tensor2
408+
409+
def broadcast_left_multiplication(self, tensor1: Tensor, tensor2: Tensor):
410+
if len(tensor1.shape) != 1:
411+
raise ValueError("only order-1 tensors are allowed for `tensor1`,"
412+
" found `tensor1.shape = {}`".format(tensor1.shape))
413+
414+
t1_broadcast_shape = self.shape_concat(
415+
[self.shape_tensor(tensor1), [1] * (len(tensor2.shape) - 1)], axis=-1)
416+
return tensor2 * self.reshape(tensor1, t1_broadcast_shape)

tensornetwork/backends/numpy/numpy_backend_test.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,3 +629,21 @@ def test_broadcast_right_multiplication_raises():
629629
tensor2 = backend.randn((3, 3), dtype=dtype, seed=10)
630630
with pytest.raises(ValueError):
631631
backend.broadcast_right_multiplication(tensor1, tensor2)
632+
633+
634+
@pytest.mark.parametrize("dtype", [np.float64, np.complex128])
635+
def test_broadcast_left_multiplication(dtype):
636+
backend = numpy_backend.NumPyBackend()
637+
tensor1 = backend.randn((3,), dtype=dtype, seed=10)
638+
tensor2 = backend.randn((3, 4, 2), dtype=dtype, seed=10)
639+
out = backend.broadcast_left_multiplication(tensor1, tensor2)
640+
np.testing.assert_allclose(out, np.reshape(tensor1, (3, 1, 1)) * tensor2)
641+
642+
643+
def test_broadcast_left_multiplication_raises():
644+
dtype = np.float64
645+
backend = numpy_backend.NumPyBackend()
646+
tensor1 = backend.randn((3, 3), dtype=dtype, seed=10)
647+
tensor2 = backend.randn((2, 4, 3), dtype=dtype, seed=10)
648+
with pytest.raises(ValueError):
649+
backend.broadcast_left_multiplication(tensor1, tensor2)

tensornetwork/backends/pytorch/pytorch_backend.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,3 +313,12 @@ def broadcast_right_multiplication(self, tensor1: Tensor, tensor2: Tensor):
313313
.format(tensor2.shape))
314314

315315
return tensor1 * tensor2
316+
317+
def broadcast_left_multiplication(self, tensor1: Tensor, tensor2: Tensor):
318+
if len(tensor1.shape) != 1:
319+
raise ValueError("only order-1 tensors are allowed for `tensor1`,"
320+
" found `tensor1.shape = {}`".format(tensor1.shape))
321+
322+
t1_broadcast_shape = self.shape_concat(
323+
[self.shape_tensor(tensor1), [1] * (len(tensor2.shape) - 1)], axis=-1)
324+
return tensor2 * self.reshape(tensor1, t1_broadcast_shape)

tensornetwork/backends/pytorch/pytorch_backend_test.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,3 +461,21 @@ def test_broadcast_right_multiplication_raises():
461461
tensor2 = backend.randn((3, 3), dtype=dtype, seed=10)
462462
with pytest.raises(ValueError):
463463
backend.broadcast_right_multiplication(tensor1, tensor2)
464+
465+
466+
def test_broadcast_left_multiplication():
467+
dtype = torch.float64
468+
backend = pytorch_backend.PyTorchBackend()
469+
tensor1 = backend.randn((3,), dtype=dtype, seed=10)
470+
tensor2 = backend.randn((3, 4, 2), dtype=dtype, seed=10)
471+
out = backend.broadcast_left_multiplication(tensor1, tensor2)
472+
np.testing.assert_allclose(out, np.reshape(tensor1, (3, 1, 1)) * tensor2)
473+
474+
475+
def test_broadcast_left_multiplication_raises():
476+
dtype = torch.float64
477+
backend = pytorch_backend.PyTorchBackend()
478+
tensor1 = backend.randn((3, 3), dtype=dtype, seed=10)
479+
tensor2 = backend.randn((2, 4, 3), dtype=dtype, seed=10)
480+
with pytest.raises(ValueError):
481+
backend.broadcast_left_multiplication(tensor1, tensor2)

tensornetwork/backends/shell/shell_backend.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,3 +332,15 @@ def broadcast_right_multiplication(self, tensor1: Tensor, tensor2: Tensor):
332332
shape2 = tuple([1] * (len(tensor1.shape) - len(shape2))) + shape2
333333
shape = tuple([max([s1, s2]) for s1, s2 in zip(tensor1.shape, shape2)])
334334
return ShellTensor(shape)
335+
336+
def broadcast_left_multiplication(self, tensor1: Tensor, tensor2: Tensor):
337+
if len(tensor1.shape) != 1:
338+
raise ValueError(
339+
"only order-1 tensors are allowed for `tensor1`, found `tensor1.shape = {}`"
340+
.format(tensor1.shape))
341+
342+
shape1 = tuple(tensor1.shape)
343+
if len(shape1) < len(tensor2.shape):
344+
shape1 = shape1 + tuple([1] * (len(tensor2.shape) - len(shape1)))
345+
shape = tuple([max([s1, s2]) for s1, s2 in zip(tensor2.shape, shape1)])
346+
return ShellTensor(shape)

tensornetwork/backends/shell/shell_backend_test.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,3 +293,19 @@ def test_broadcast_right_multiplication_raises():
293293
tensor2 = backend.randn((3, 3))
294294
with pytest.raises(ValueError):
295295
backend.broadcast_right_multiplication(tensor1, tensor2)
296+
297+
298+
def test_broadcast_left_multiplication():
299+
backend = shell_backend.ShellBackend()
300+
tensor1 = backend.randn((3,))
301+
tensor2 = backend.randn((3, 4, 2))
302+
out = backend.broadcast_left_multiplication(tensor1, tensor2)
303+
np.testing.assert_allclose(out.shape, [3, 4, 2])
304+
305+
306+
def test_broadcast_left_multiplication_raises():
307+
backend = shell_backend.ShellBackend()
308+
tensor1 = backend.randn((3, 3))
309+
tensor2 = backend.randn((3, 4, 2))
310+
with pytest.raises(ValueError):
311+
backend.broadcast_left_multiplication(tensor1, tensor2)

tensornetwork/backends/symmetric/symmetric_backend.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -196,9 +196,11 @@ def broadcast_right_multiplication(self, tensor1: Tensor, tensor2: Tensor):
196196
if tensor2.ndim != 1:
197197
raise ValueError("only order-1 tensors are allowed for `tensor2`,"
198198
" found `tensor2.shape = {}`".format(tensor2.shape))
199-
shape1 = tensor1.shape
200-
tmp = self.reshape(tensor1,
201-
(numpy.prod(shape1[:tensor1.ndim - 1]), shape1[-1]))
202-
#NOTE (mganahl): we use mulitplication by diagonal matrix here
203-
return self.reshape(
204-
self.tensordot(tmp, self.diag(tensor2), ([1], [0])), shape1)
199+
return self.tensordot(tensor1, self.diag(tensor2),
200+
([len(tensor1.shape) - 1], [0]))
201+
202+
def broadcast_left_multiplication(self, tensor1: Tensor, tensor2: Tensor):
203+
if len(tensor1.shape) != 1:
204+
raise ValueError("only order-1 tensors are allowed for `tensor1`,"
205+
" found `tensor1.shape = {}`".format(tensor1.shape))
206+
return self.tensordot(self.diag(tensor1), tensor2, ([1], [0]))

tensornetwork/backends/symmetric/symmetric_backend_test.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -739,3 +739,45 @@ def test_broadcast_right_multiplication_raises():
739739
tensor2 = ChargeArray.random(indices=indices)
740740
with pytest.raises(ValueError):
741741
backend.broadcast_right_multiplication(tensor1, tensor2)
742+
743+
744+
@pytest.mark.parametrize("dtype", [np.float64, np.complex128])
745+
@pytest.mark.parametrize("num_charges", [1, 2])
746+
def test_broadcast_left_multiplication(dtype, num_charges):
747+
np.random.seed(10)
748+
backend = symmetric_backend.SymmetricBackend()
749+
Ds = [10, 30, 24]
750+
R = len(Ds)
751+
indices = [
752+
Index(
753+
BaseCharge(
754+
np.random.randint(-5, 6, (num_charges, Ds[n])),
755+
charge_types=[U1Charge] * num_charges), False) for n in range(R)
756+
]
757+
758+
tensor1 = ChargeArray.random(indices=[indices[0]], dtype=dtype)
759+
tensor2 = backend.randn(indices, dtype=dtype)
760+
t1dense = tensor1.todense()
761+
t2dense = tensor2.todense()
762+
out = backend.broadcast_left_multiplication(tensor1, tensor2)
763+
dense = np.reshape(t1dense, (10, 1, 1)) * t2dense
764+
np.testing.assert_allclose(out.todense(), dense)
765+
766+
767+
def test_broadcast_left_multiplication_raises():
768+
np.random.seed(10)
769+
backend = symmetric_backend.SymmetricBackend()
770+
num_charges = 1
771+
Ds = [10, 30, 24]
772+
R = len(Ds)
773+
indices = [
774+
Index(
775+
BaseCharge(
776+
np.random.randint(-5, 6, (num_charges, Ds[n])),
777+
charge_types=[U1Charge] * num_charges), False) for n in range(R)
778+
]
779+
780+
tensor1 = ChargeArray.random(indices=indices)
781+
tensor2 = backend.randn(indices)
782+
with pytest.raises(ValueError):
783+
backend.broadcast_left_multiplication(tensor1, tensor2)

0 commit comments

Comments
 (0)