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

Commit 5d6ffc9

Browse files
IllgamhoDuckChase Roberts
andcommitted
Algebraic operation add( + ), sub( - ), mul( * ), div( / ) for BaseNode class (#439)
* BaseNode / Edge class text input protection added (#423) BaseNode class - Add protection to name, axis_names *Protected in 3 place *Initialize stage - __init__ *Function use setting - set_name / add_axis_names *Property - Add @Property to name to protect direct adding node.name = 123 Edge class - Add protection to name *Protected in 3 place *Initialize stage - __init__ *Function use setting - set_name *Property * BaseNode / Edge class text input protection code revise (#423) *if type(name) != str *if not isinstance(name, str) *change using type to isinstance to follow pylint * Algebraic operation add( + ), sub( - ), mul( * ), div( / ) for BaseNode class (#292) *[BaseNode class] - add / sub / mul / truediv NotImplemented function Added *[Node class] - add / sub / mul / truediv function added *[CopyNode class] - overload the BaseNode mul / truediv as NotImplemented *[basebackend] - add / sub / mul / div NotImplemented function added *[numpy / tensorflow / pytorch] - add / sub / mul / div function added *[shell] - add / sub / div NotImplemented function added *Testing files [network_components_free_test] * Exception - Tensorflow is not tested when the operand is scalar * 1. Check add / sub / mul / div with int / float / Node * 2. Check implicit conversion * 2. Check the Type Error when type is not int / float / Node * 3. Check is the operand backend same * 4. Check is BaseNode has attribute _tensor [backend_test - numpy / tensorflow / pytorch] *check add / sub / mul / divide work for int / float / Node * Add test cases for Tensorflow Algebraic operation and fix add, sub name (#292) [Change name] *add -> addition *subtract -> substraction [Add test case for Tensorflow] * Specify the datatype to resolve the conflict between different dtype operation [Test case for pytorch / jax] * pytorch - [int / int -> int] give different answer for torch when it is dividing two integer * jax - Different from other backend jax backend return 64bits dtype even operate between 32bits so put exceptional dtype test case for jax backend * Add test cases for Tensorflow Algebraic operation and fix add, sub name (#292) [Change name] *add -> addition *subtract -> substraction [Add test case for Tensorflow] * Specify the datatype to resolve the conflict between different dtype operation [Test case for pytorch / jax] * pytorch - [int / int -> int] give different answer for torch when it is dividing two integer * jax - Different from other backend jax backend return 64bits dtype even operate between 32bits so put exceptional dtype test case for jax backend * Add __add__, __sub__, __mul__, __truediv__ to TestNode Class Co-authored-by: Chase Roberts <chaseriley@google.com>
1 parent a159393 commit 5d6ffc9

10 files changed

Lines changed: 638 additions & 7 deletions

tensornetwork/backends/base_backend.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,32 @@ def eigsh_lanczos(self,
391391
raise NotImplementedError(
392392
"Backend '{}' has not implemented eighs_lanczos.".format(self.name))
393393

394+
def addition(self, tensor1: Tensor, tensor2: Tensor) -> Tensor:
395+
"""
396+
Return the default multiplication of `tensor`.
397+
A backend can override such implementation.
398+
Args:
399+
tensor1: A tensor.
400+
tensor2: A tensor.
401+
Returns:
402+
Tensor
403+
"""
404+
raise NotImplementedError(
405+
"Backend '{}' has not implemented addition.".format(self.name))
406+
407+
def subtraction(self, tensor1: Tensor, tensor2: Tensor) -> Tensor:
408+
"""
409+
Return the default multiplication of `tensor`.
410+
A backend can override such implementation.
411+
Args:
412+
tensor1: A tensor.
413+
tensor2: A tensor.
414+
Returns:
415+
Tensor
416+
"""
417+
raise NotImplementedError(
418+
"Backend '{}' has not implemented subtraction.".format(self.name))
419+
394420
def multiply(self, tensor1: Tensor, tensor2: Tensor) -> Tensor:
395421
"""
396422
Return the default multiplication of `tensor`.
@@ -404,6 +430,19 @@ def multiply(self, tensor1: Tensor, tensor2: Tensor) -> Tensor:
404430
raise NotImplementedError(
405431
"Backend '{}' has not implemented multiply.".format(self.name))
406432

433+
def divide(self, tensor1: Tensor, tensor2: Tensor) -> Tensor:
434+
"""
435+
Return the default divide of `tensor`.
436+
A backend can override such implementation.
437+
Args:
438+
tensor1: A tensor.
439+
tensor2: A tensor.
440+
Returns:
441+
Tensor
442+
"""
443+
raise NotImplementedError(
444+
"Backend '{}' has not implemented divide.".format(self.name))
445+
407446
def index_update(self, tensor: Tensor, mask: Tensor,
408447
assignee: Tensor) -> Tensor:
409448
"""

tensornetwork/backends/numpy/numpy_backend.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,9 +371,18 @@ def eigsh_lanczos(self,
371371
eigenvectors.append(state / self.np.linalg.norm(state))
372372
return eigvals[0:numeig], eigenvectors
373373

374+
def addition(self, tensor1: Tensor, tensor2: Tensor) -> Tensor:
375+
return tensor1 + tensor2
376+
377+
def subtraction(self, tensor1: Tensor, tensor2: Tensor) -> Tensor:
378+
return tensor1 - tensor2
379+
374380
def multiply(self, tensor1: Tensor, tensor2: Tensor) -> Tensor:
375381
return tensor1 * tensor2
376382

383+
def divide(self, tensor1: Tensor, tensor2: Tensor) -> Tensor:
384+
return tensor1 / tensor2
385+
377386
def index_update(self, tensor: Tensor, mask: Tensor,
378387
assignee: Tensor) -> Tensor:
379388
t = self.np.copy(tensor)

tensornetwork/backends/numpy/numpy_backend_test.py

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -321,15 +321,67 @@ def test_eigsh_lanczos_raises():
321321

322322

323323
@pytest.mark.parametrize("a, b, expected", [
324+
pytest.param(1, 1, 2),
325+
pytest.param(1., np.ones((1, 2, 3)), 2*np.ones((1, 2, 3))),
326+
pytest.param(2.*np.ones(()), 1., 3.*np.ones((1, 2, 3))),
327+
pytest.param(2.*np.ones(()), 1.*np.ones((1, 2, 3)), 3.*np.ones((1, 2, 3))),
328+
])
329+
def test_addition(a, b, expected):
330+
backend = numpy_backend.NumPyBackend()
331+
tensor1 = backend.convert_to_tensor(a)
332+
tensor2 = backend.convert_to_tensor(b)
333+
result = backend.addition(tensor1, tensor2)
334+
335+
np.testing.assert_allclose(result, expected)
336+
assert tensor1.dtype == tensor2.dtype == result.dtype
337+
338+
339+
@pytest.mark.parametrize("a, b, expected", [
340+
pytest.param(1, 1, 0),
341+
pytest.param(2., 1.*np.ones((1, 2, 3)), 1.*np.ones((1, 2, 3))),
342+
pytest.param(np.ones((1, 2, 3)), 1., np.zeros((1, 2, 3))),
343+
pytest.param(np.ones((1, 2, 3)), np.ones((1, 2, 3)), np.zeros((1, 2, 3))),
344+
])
345+
def test_subtraction(a, b, expected):
346+
backend = numpy_backend.NumPyBackend()
347+
tensor1 = backend.convert_to_tensor(a)
348+
tensor2 = backend.convert_to_tensor(b)
349+
result = backend.subtraction(tensor1, tensor2)
350+
351+
np.testing.assert_allclose(result, expected)
352+
assert tensor1.dtype == tensor2.dtype == result.dtype
353+
354+
355+
@pytest.mark.parametrize("a, b, expected", [
356+
pytest.param(1, 1, 1),
357+
pytest.param(2., 1.*np.ones((1, 2, 3)), 2.*np.ones((1, 2, 3))),
358+
pytest.param(np.ones((1, 2, 3)), 1., np.ones((1, 2, 3))),
324359
pytest.param(np.ones((1, 2, 3)), np.ones((1, 2, 3)), np.ones((1, 2, 3))),
325-
pytest.param(2. * np.ones(()), np.ones((1, 2, 3)), 2. * np.ones((1, 2, 3))),
326360
])
327361
def test_multiply(a, b, expected):
328362
backend = numpy_backend.NumPyBackend()
329363
tensor1 = backend.convert_to_tensor(a)
330364
tensor2 = backend.convert_to_tensor(b)
365+
result = backend.multiply(tensor1, tensor2)
366+
367+
np.testing.assert_allclose(result, expected)
368+
assert tensor1.dtype == tensor2.dtype == result.dtype
369+
370+
371+
@pytest.mark.parametrize("a, b, expected", [
372+
pytest.param(2., 2., 1.),
373+
pytest.param(2., 0.5*np.ones((1, 2, 3)), 4.*np.ones((1, 2, 3))),
374+
pytest.param(np.ones(()), 2., 0.5*np.ones((1, 2, 3))),
375+
pytest.param(np.ones(()), 2.*np.ones((1, 2, 3)), 0.5*np.ones((1, 2, 3))),
376+
])
377+
def test_divide(a, b, expected):
378+
backend = numpy_backend.NumPyBackend()
379+
tensor1 = backend.convert_to_tensor(a)
380+
tensor2 = backend.convert_to_tensor(b)
381+
result = backend.divide(tensor1, tensor2)
331382

332-
np.testing.assert_allclose(backend.multiply(tensor1, tensor2), expected)
383+
np.testing.assert_allclose(result, expected)
384+
assert tensor1.dtype == tensor2.dtype == result.dtype
333385

334386

335387
def find(which, vector):

tensornetwork/backends/pytorch/pytorch_backend.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,9 +275,18 @@ def eigsh_lanczos(self,
275275
eigenvectors.append(state / self.torch.norm(state))
276276
return eigvals[0:numeig], eigenvectors
277277

278+
def addition(self, tensor1: Tensor, tensor2: Tensor) -> Tensor:
279+
return tensor1 + tensor2
280+
281+
def subtraction(self, tensor1: Tensor, tensor2: Tensor) -> Tensor:
282+
return tensor1 - tensor2
283+
278284
def multiply(self, tensor1: Tensor, tensor2: Tensor) -> Tensor:
279285
return tensor1 * tensor2
280286

287+
def divide(self, tensor1: Tensor, tensor2: Tensor) -> Tensor:
288+
return tensor1 / tensor2
289+
281290
def index_update(self, tensor: Tensor, mask: Tensor,
282291
assignee: Tensor) -> Tensor:
283292
#make a copy

tensornetwork/backends/pytorch/pytorch_backend_test.py

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -293,15 +293,59 @@ def test_eigsh_lanczos_raises():
293293

294294

295295
@pytest.mark.parametrize("a, b, expected", [
296+
pytest.param(1, 1, 2),
297+
pytest.param(np.ones((1, 2, 3)), np.ones((1, 2, 3)), 2.*np.ones((1, 2, 3))),
298+
])
299+
def test_addition(a, b, expected):
300+
backend = pytorch_backend.PyTorchBackend()
301+
tensor1 = backend.convert_to_tensor(a)
302+
tensor2 = backend.convert_to_tensor(b)
303+
result = backend.addition(tensor1, tensor2)
304+
305+
np.testing.assert_allclose(result, expected)
306+
assert tensor1.dtype == tensor2.dtype == result.dtype
307+
308+
309+
@pytest.mark.parametrize("a, b, expected", [
310+
pytest.param(1, 1, 0),
311+
pytest.param(np.ones((1, 2, 3)), np.ones((1, 2, 3)), np.zeros((1, 2, 3))),
312+
])
313+
def test_subtraction(a, b, expected):
314+
backend = pytorch_backend.PyTorchBackend()
315+
tensor1 = backend.convert_to_tensor(a)
316+
tensor2 = backend.convert_to_tensor(b)
317+
result = backend.subtraction(tensor1, tensor2)
318+
319+
np.testing.assert_allclose(result, expected)
320+
assert tensor1.dtype == tensor2.dtype == result.dtype
321+
322+
323+
@pytest.mark.parametrize("a, b, expected", [
324+
pytest.param(1, 1, 1),
296325
pytest.param(np.ones((1, 2, 3)), np.ones((1, 2, 3)), np.ones((1, 2, 3))),
297-
pytest.param(2. * np.ones(()), np.ones((1, 2, 3)), 2. * np.ones((1, 2, 3))),
298326
])
299327
def test_multiply(a, b, expected):
300328
backend = pytorch_backend.PyTorchBackend()
301329
tensor1 = backend.convert_to_tensor(a)
302330
tensor2 = backend.convert_to_tensor(b)
331+
result = backend.multiply(tensor1, tensor2)
332+
333+
np.testing.assert_allclose(result, expected)
334+
assert tensor1.dtype == tensor2.dtype == result.dtype
335+
336+
337+
@pytest.mark.parametrize("a, b, expected", [
338+
pytest.param(2., 2., 1.),
339+
pytest.param(np.ones(()), 2.*np.ones((1, 2, 3)), 0.5*np.ones((1, 2, 3))),
340+
])
341+
def test_divide(a, b, expected):
342+
backend = pytorch_backend.PyTorchBackend()
343+
tensor1 = backend.convert_to_tensor(a)
344+
tensor2 = backend.convert_to_tensor(b)
345+
result = backend.divide(tensor1, tensor2)
303346

304-
np.testing.assert_allclose(backend.multiply(tensor1, tensor2), expected)
347+
np.testing.assert_allclose(result, expected)
348+
assert tensor1.dtype == tensor2.dtype == result.dtype
305349

306350

307351
def test_eigh():

tensornetwork/backends/shell/shell_backend.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,11 +290,20 @@ def eigsh_lanczos(self,
290290
raise ValueError(
291291
'`A` has no attribut shape adn no `initial_state` is given.')
292292

293+
def addition(self, tensor1: Tensor, tensor2: Tensor) -> Tensor:
294+
raise NotImplementedError("Shell tensor has not implemented addition( + )")
295+
296+
def subtraction(self, tensor1: Tensor, tensor2: Tensor) -> Tensor:
297+
raise NotImplementedError("Shell tensor has not implemented subtraction( - )")
298+
293299
def multiply(self, tensor1: Tensor, tensor2: Tensor) -> Tensor:
294300
a = np.ones(tensor1.shape)
295301
b = np.ones(tensor2.shape)
296302
return ShellTensor((a * b).shape)
297303

304+
def divide(self, tensor1: Tensor, tensor2: Tensor) -> Tensor:
305+
raise NotImplementedError("Shell tensor has not implemented add( / )")
306+
298307
def index_update(self, tensor: Tensor, mask: Tensor,
299308
assignee: Tensor) -> Tensor:
300309
return ShellTensor(tensor.shape)

tensornetwork/backends/tensorflow/tensorflow_backend.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,9 +182,18 @@ def eigsh_lanczos(self,
182182
raise NotImplementedError(
183183
"Backend '{}' has not implemented eighs_lanczos.".format(self.name))
184184

185+
def addition(self, tensor1: Tensor, tensor2: Tensor) -> Tensor:
186+
return tensor1 + tensor2
187+
188+
def subtraction(self, tensor1: Tensor, tensor2: Tensor) -> Tensor:
189+
return tensor1 - tensor2
190+
185191
def multiply(self, tensor1: Tensor, tensor2: Tensor) -> Tensor:
186192
return tensor1 * tensor2
187193

194+
def divide(self, tensor1: Tensor, tensor2: Tensor) -> Tensor:
195+
return tensor1 / tensor2
196+
188197
def index_update(self, tensor: Tensor, mask: Tensor,
189198
assignee: Tensor) -> Tensor:
190199
#returns a copy (unfortunately)

tensornetwork/backends/tensorflow/tensorflow_backend_test.py

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -247,15 +247,59 @@ def test_conj():
247247

248248

249249
@pytest.mark.parametrize("a, b, expected", [
250+
pytest.param(1, 1, 2),
251+
pytest.param(2.*np.ones(()), 1.*np.ones((1, 2, 3)), 3.*np.ones((1, 2, 3))),
252+
])
253+
def test_addition(a, b, expected):
254+
backend = tensorflow_backend.TensorFlowBackend()
255+
tensor1 = backend.convert_to_tensor(a)
256+
tensor2 = backend.convert_to_tensor(b)
257+
result = backend.addition(tensor1, tensor2)
258+
259+
np.testing.assert_allclose(result, expected)
260+
assert tensor1.dtype == tensor2.dtype == result.dtype
261+
262+
263+
@pytest.mark.parametrize("a, b, expected", [
264+
pytest.param(1, 1, 0),
265+
pytest.param(np.ones((1, 2, 3)), np.ones((1, 2, 3)), np.zeros((1, 2, 3))),
266+
])
267+
def test_subtraction(a, b, expected):
268+
backend = tensorflow_backend.TensorFlowBackend()
269+
tensor1 = backend.convert_to_tensor(a)
270+
tensor2 = backend.convert_to_tensor(b)
271+
result = backend.subtraction(tensor1, tensor2)
272+
273+
np.testing.assert_allclose(result, expected)
274+
assert tensor1.dtype == tensor2.dtype == result.dtype
275+
276+
277+
@pytest.mark.parametrize("a, b, expected", [
278+
pytest.param(1, 1, 1),
250279
pytest.param(np.ones((1, 2, 3)), np.ones((1, 2, 3)), np.ones((1, 2, 3))),
251-
pytest.param(2. * np.ones(()), np.ones((1, 2, 3)), 2. * np.ones((1, 2, 3))),
252280
])
253281
def test_multiply(a, b, expected):
254282
backend = tensorflow_backend.TensorFlowBackend()
255283
tensor1 = backend.convert_to_tensor(a)
256284
tensor2 = backend.convert_to_tensor(b)
285+
result = backend.multiply(tensor1, tensor2)
286+
287+
np.testing.assert_allclose(result, expected)
288+
assert tensor1.dtype == tensor2.dtype == result.dtype
289+
290+
291+
@pytest.mark.parametrize("a, b, expected", [
292+
pytest.param(2., 2., 1.),
293+
pytest.param(np.ones(()), 2.*np.ones((1, 2, 3)), 0.5*np.ones((1, 2, 3))),
294+
])
295+
def test_divide(a, b, expected):
296+
backend = tensorflow_backend.TensorFlowBackend()
297+
tensor1 = backend.convert_to_tensor(a)
298+
tensor2 = backend.convert_to_tensor(b)
299+
result = backend.divide(tensor1, tensor2)
257300

258-
np.testing.assert_allclose(backend.multiply(tensor1, tensor2), expected)
301+
np.testing.assert_allclose(result, expected)
302+
assert tensor1.dtype == tensor2.dtype == result.dtype
259303

260304

261305
@pytest.mark.parametrize("dtype", [tf.float64, tf.complex128])

0 commit comments

Comments
 (0)