| 987 | |
| 988 | |
| 989 | def test_modify_struct(): |
| 990 | |
| 991 | @dataclass(slots=True) |
| 992 | class MyClass: |
| 993 | x: int |
| 994 | y: bool |
| 995 | |
| 996 | with pytest.raises(RuntimeError) as e: |
| 997 | |
| 998 | @cudaq.kernel |
| 999 | def simple_struc_err(t: MyClass) -> MyClass: |
| 1000 | q = cudaq.qubit() |
| 1001 | # If we allowed this, the expected behavior for Python |
| 1002 | # would be that t is modified also in the caller without |
| 1003 | # having to return it. We hence give an error to make it |
| 1004 | # clear that changes to structs don't propagate past |
| 1005 | # function boundaries. |
| 1006 | t.x = 42 |
| 1007 | return t |
| 1008 | |
| 1009 | cudaq.run(simple_struc_err, MyClass(-13, True), shots_count=2) |
| 1010 | |
| 1011 | assert 'value cannot be modified - use `.copy(deep)` to create a new value that can be modified' in repr( |
| 1012 | e) |
| 1013 | assert '(offending source -> t.x)' in repr(e) |
| 1014 | |
| 1015 | @cudaq.kernel |
| 1016 | def simple_structA(arg: MyClass) -> MyClass: |
| 1017 | q = cudaq.qubit() |
| 1018 | t = arg.copy() |
| 1019 | t.x = 42 |
| 1020 | return t |
| 1021 | |
| 1022 | results = cudaq.run(simple_structA, MyClass(-13, True), shots_count=2) |
| 1023 | print(results) |
| 1024 | assert len(results) == 2 |
| 1025 | assert results[0] == MyClass(42, True) |
| 1026 | assert results[1] == MyClass(42, True) |
| 1027 | |
| 1028 | @dataclass(slots=True) |
| 1029 | class Foo: |
| 1030 | x: bool |
| 1031 | y: float |
| 1032 | z: int |
| 1033 | |
| 1034 | @cudaq.kernel |
| 1035 | def kernelB(arg: Foo) -> Foo: |
| 1036 | q = cudaq.qubit() |
| 1037 | t = arg.copy() |
| 1038 | t.z = 100 |
| 1039 | t.y = 3.14 |
| 1040 | t.x = True |
| 1041 | return t |
| 1042 | |
| 1043 | results = cudaq.run(kernelB, Foo(False, 6.28, 17), shots_count=2) |
| 1044 | print(results) |
| 1045 | assert len(results) == 2 |
| 1046 | assert results[0] == Foo(True, 3.14, 100) |