def test_DUInt_type(self): self.assertEqual(DUInt(DSize(8)).as_numpy_type(), np.uint8) self.assertEqual(DUInt(DSize(16)).as_numpy_type(), np.uint16) self.assertEqual(DUInt(DSize(32)).as_numpy_type(), np.uint32) self.assertEqual(DUInt(DSize(64)).as_numpy_type(), np.uint64) with self.assertRaises(NotImplementedError): DUInt(DSize(128)).as_numpy_type()
def test_types_comparison(self): """Various tests of types comparison.""" # primitive self.assertEqual(DInt(DSize(32)), DInt()) self.assertNotEqual(DInt(), DUInt()) self.assertNotEqual(DInt(), DInt(DSize(64))) # compound self.assertEqual(DTuple([int, bool]), DTuple([int, bool])) self.assertNotEqual(DTuple([int, bool]), DTuple([bool, int])) self.assertEqual(DArray(int, DSize(4)), DArray(int, DSize(4))) self.assertEqual(DArray(int, DSize(4)), DArray(DInt(), DSize(4))) self.assertNotEqual(DArray(int, DSize(4)), DArray(int, DSize(5))) self.assertNotEqual(DStr(), DStr(DSize(100))) self.assertEqual(DRecord(RecBI), DRecord(RecBI)) # compound: DUnion self.assertEqual(DUnion([int, bool]), DUnion([bool, int])) self.assertEqual(DUnion([int, DUnion([int, bool])]), DUnion([int, bool])) self.assertEqual(DUnion([int, DUnion([int, DUnion([int, bool])])]), DUnion([int, bool])) self.assertEqual(DUnion([int, int]), DUnion([int])) self.assertNotEqual(DUnion([DInt()]), DInt()) # special self.assertEqual(ForkedReturn(dict(x=int, y=bool, z=str)), ForkedReturn(dict(x=int, y=bool, z=str)))
def test_DComplex(self): """Only 64 and 128 bits are supported.""" for bits in (64, 128): for _ in range(1000): self.check_complex(random.uniform(-1, 1) + random.uniform(-1, 1) * 1j, DComplex(DSize(bits))) self.assertTrue(DeltaGraph.check_wire(DRaw(complex), DRaw(complex))) with self.assertRaises(DeltaTypeError): DeltaGraph.check_wire(DRaw(DComplex(DSize(64))), DRaw(DComplex(DSize(128))))
def accept_command( self, command: DUInt(DSize(32)) ) -> DUInt(DSize(32)): op = command >> Shifts.OPCODE.value qubit_index = (command & Masks.QUBIT_INDEX.value) if qubit_index + 1 > self._qubit_register_size: raise ValueError( f"Qubit index ({qubit_index}) greater than " + f"register size ({self._qubit_register_size})!" ) if op == Opcode["STATE_PREPARATION"].value: if not self._qubit_register: self._qubit_register = self._engine.allocate_qureg( self._qubit_register_size ) elif op == Opcode["STATE_MEASURE"].value: All(Measure) | self._qubit_register self._engine.flush() # Each measurement sent should have all valid flags. # Therefore valid mask added to the 16bit measurement bitcode. measurement_binary = Masks.VALIDS.value for n, i in enumerate(self._qubit_register, 0): m = int(i) measurement_binary += m*2**n self._qubit_register = None return measurement_binary elif op in self._parameterised_gate_dict.keys(): angle = (command & Masks.ARG.value) >> Shifts.ARG.value angle *= (2 * np.pi) / 1024 gate = self._parameterised_gate_dict[op] self.apply_gate(gate, qubit_index, angle) elif op in self._constant_gate_dict.keys(): gate = self._constant_gate_dict[op] self.apply_gate(gate, qubit_index) elif op == Opcode['ID'].value: pass else: raise TypeError(f"{op} is not a recognised opcode!")
def test_DInt(self): """Only 8, 16, 32 and 64 bits are supported.""" for bits in (8, 16, 32, 64): self.check(-2**(bits-1), DInt(DSize(bits))) self.check(2**(bits-1) - 1, DInt(DSize(bits))) for _ in range(1000): self.check(random.randint(-2**(bits-1), 2**(bits-1) - 1), DInt(DSize(bits))) self.assertTrue(DeltaGraph.check_wire(DRaw(int), DRaw(int))) with self.assertRaises(DeltaTypeError): DeltaGraph.check_wire(DRaw(DInt(DSize(32))), DRaw(DInt(DSize(64))))
def test_DStr(self): self.check('hello world', DStr()) self.check('A' * 1024, DStr()) self.check('check digits 14213', DStr()) self.check('check spaces in the end ', DStr()) with self.assertRaises(DeltaTypeError): self.check('123456', DStr(DSize(4))) self.check((-5, 'text'), DTuple([int, DStr()])) self.check(['hello', 'world!'], DArray(DStr(), DSize(2))) self.check_numpy('hello world', DStr())
def test_DUnion(self): # primitive self.check(5, DUnion([int, bool])) self.check(True, DUnion([int, bool])) # compound self.check(5, DUnion([int, DTuple([int, float])])) self.check((4, 5), DUnion([int, DTuple([int, int])])) self.check((4, 5), DUnion([DArray(int, DSize(2)), DTuple([int, int])])) self.check([4, 5], DUnion([DArray(int, DSize(2)), DTuple([int, int])])) self.assertTrue(DeltaGraph.check_wire(DRaw(DUnion([DStr(), int])), DRaw(DUnion([DStr(), int]))))
def test_DTuple(self): # primitive elements are poperly handled self.check((-5, True, 3.25), DTuple([int, bool, float])) # incapsulation self.check((-5, (1, 2)), DTuple([int, DTuple([int, int])])) # mixed types self.check(([1, 2, 3], [4.0, 5.0]), DTuple([DArray(int, DSize(3)), DArray(float, DSize(2))])) self.check(("hello", "world"), DTuple([DStr(), DStr(DSize(6))])) self.assertTrue(DeltaGraph.check_wire(DRaw(DTuple([DStr(), int])), DRaw(DTuple([DStr(), int]))))
def test_DFloat(self): """Only 32 and 64 bits are supported.""" precision_dict = {32: -23, 64: -52} for bits, precision in precision_dict.items(): for _ in range(1000): self.check_float(random.uniform(-1, 1), DFloat(DSize(bits))) self.check(1 + 2**precision, DFloat(DSize(bits))) self.assertTrue(DeltaGraph.check_wire(DRaw(float), DRaw(float))) with self.assertRaises(DeltaTypeError): DeltaGraph.check_wire(DRaw(DFloat(DSize(32))), DRaw(DFloat(DSize(64))))
def check_float(self, val, t: DFloat): """Test pack-unpack for floats.""" if not isinstance(t, DFloat): raise DeltaTypeError # TODO this check can be done on binary, then the number of places # will be more reasonably explained if t.size == DSize(32): places = 6 elif t.size == DSize(64): places = 15 else: raise NotImplementedError('Unsupported format') val_new = self.to_np_and_back(val, t) self.assertAlmostEqual(val, val_new, places=places)
def check_complex(self, val, t: DComplex): """Test pack-unpack for complex numbers.""" if not isinstance(t, DComplex): raise DeltaTypeError # Using the same idea as check_float, but for real and imaginary parts if t.size == DSize(64): places = 6 elif t.size == DSize(128): places = 15 else: raise NotImplementedError('Unsupported format') val_new = self.to_np_and_back(val, t) self.assertAlmostEqual(val.real, val_new.real, places=places) self.assertAlmostEqual(val.imag, val_new.imag, places=places)
def command_unpacker(cmd: DUInt(DSize(32))): """Helper function to unpack HAL commands. Parameters ---------- cmd : DUInt(DSize(32)) 32-bit HAL command. Returns ------- op : str Name of opcode. argument : int Integer representation of argument value. qubit : int Integer representation of qubit address. """ op_mask = sum(map(lambda n: 2**n, range(Shifts.OPCODE.value, 32))) op = (cmd & op_mask) >> Shifts.OPCODE.value # We pass strings around rather than Opcode elements, so we use # this to get the reverse mapping from the Opcode class # This is semi-safe, as we don't have repeated names for operations op = Opcode._value2member_map_[op]._name_ arg_mask = sum( map(lambda n: 2**n, range(Shifts.ARG.value, Shifts.OPCODE.value))) arg = (cmd & arg_mask) >> Shifts.ARG.value qubit_mask = sum(map(lambda n: 2**n, range(0, Shifts.VALIDS.value))) qubit = (cmd & qubit_mask) return (op, arg, qubit)
def accept_command( self, hal_command: DUInt(DSize(32))) -> DUInt(DSize(32)): """Interface for ``quantum_simulator.accept_command`` that is used to create a graph node. Parameters ---------- command : DUInt(DSize(32)) The HAL command to deconstruct and use to perform actions. Returns ------- DUInt(DSize(32)) Result of a measurement command. """ result = self._quantum_simulator.accept_command(hal_command) return result
def test_DStr(self): self.check('hello world', DStr()) self.check('A' * 1024, DStr()) self.check('check digits 14213', DStr()) self.check('check spaces in the end ', DStr()) self.check((-5, 'text'), DTuple([int, DStr()])) self.check(['hello', 'world!'], DArray(DStr(), DSize(2))) self.assertTrue(DeltaGraph.check_wire(DRaw(DStr()), DRaw(DStr())))
def accept_command( cls, command: DUInt(DSize(32)) ) -> DUInt(DSize(32)): """Performs required logic based on received commands, and returns results if command is a measurement. Parameters ---------- command : DUInt(DSize(32)) The HAL command to deconstruct and use to perform actions. Returns ------- DUInt(DSize(32)) Result of a measurement command. """ pass
def test_DUInt(self): """Only 8, 16, 32, and 64 bits are supported.""" for bits in (8, 16, 32, 64): self.check(0, DUInt(DSize(bits))) self.check(2**bits - 1, DUInt(DSize(bits))) for _ in range(1000): self.check(random.randint(0, 2**bits - 1), DUInt(DSize(bits))) # Booleans can be packed self.check(True, DUInt(DSize(bits))) self.check(False, DUInt(DSize(bits))) # Floats, strings and negative or complex numbers are not packable self.assertFalse(DUInt(DSize(bits)).is_packable(3.5)) self.assertFalse(DUInt(DSize(bits)).is_packable(3.0)) self.assertFalse(DUInt(DSize(bits)).is_packable("abc")) self.assertFalse(DUInt(DSize(bits)).is_packable(-2)) self.assertFalse(DUInt(DSize(bits)).is_packable(3+5j))
def test_compound_objects(self): t = DArray(DTuple([bool, int]), DSize(3)) val = [(True, 1), (False, 2), (True, 3), (False, 4), (True, 5)] self.check(val, t) t = DTuple([int, DTuple([bool, int])]) val = (12, (True, 8)) self.check(val, t) t = DTuple([int, DArray(int, DSize(2))]) val = (12, [14, 18]) self.check(val, t) t = DTuple([int, DStr()]) val = (12, "hello") self.check(val, t) t = DTuple([int, DRecord(RecBI)]) val = (12, RecBI(True, 8)) self.check(val, t) t = DTuple([int, DUnion([bool, int])]) val = (12, True) np_val = t.as_numpy_object(val) self.assertEqual(DInt().from_numpy_object(np_val[0][0]), 12) self.assertEqual(DBool().from_numpy_object(np_val[0][1][1]), True) t = DRecord(RecATI) val = RecATI([1, 2], (3.0, 4), 5) self.check(val, t) t = DUnion([DArray(int, DSize(2)), int]) val = [1, 2] np_val = t.as_numpy_object(val) new_val = DArray(int, DSize(2)).from_numpy_object(np_val[0][1]) self.assertEqual(val, new_val) t = DUnion([str, int]) val = "abcde" np_val = t.as_numpy_object(val) new_val = DStr().from_numpy_object(np_val[0][1]) self.assertEqual(val, new_val)
def test_as_python_type(self): """Test conversion of Deltaflow data types to python.""" # special self.assertEqual(Top().as_python_type(), Any) # primitive self.assertEqual(DInt(DSize(32)).as_python_type(), int) self.assertEqual(DInt(DSize(64)).as_python_type(), int) self.assertEqual(DUInt(DSize(32)).as_python_type(), int) self.assertEqual(DUInt(DSize(64)).as_python_type(), int) self.assertEqual(DBool().as_python_type(), bool) with self.assertRaises(NotImplementedError): DChar().as_python_type() self.assertEqual(DFloat(DSize(32)).as_python_type(), float) self.assertEqual(DFloat(DSize(64)).as_python_type(), float) self.assertEqual(DComplex(DSize(64)).as_python_type(), complex) self.assertEqual(DComplex(DSize(128)).as_python_type(), complex) # compound self.assertEqual(DTuple([int, bool]).as_python_type(), Tuple[int, bool]) self.assertEqual(DTuple([int, DTuple([int, bool])]).as_python_type(), Tuple[int, Tuple[int, bool]]) self.assertEqual(DArray(int, DSize(3)).as_python_type(), List[int]) self.assertEqual(DStr().as_python_type(), str) self.assertEqual(DStr(DSize(10)).as_python_type(), str) self.assertEqual(DRecord(RecBI).as_python_type(), RecBI) self.assertEqual(DRecord(RecBDi).as_python_type(), RecBDi) self.assertNotEqual(DRecord(RecBI).as_python_type(), RecBI_copy) # compound: DUnion self.assertEqual(DUnion([bool, int]).as_python_type(), Union[bool, int]) self.assertEqual(DUnion([bool, DTuple([int, bool])]).as_python_type(), Union[bool, Tuple[int, bool]])
def test_Top(self): """Everything can be accepted as Top().""" self.assertTrue(DeltaGraph.check_wire(DInt(), Top())) self.assertTrue(DeltaGraph.check_wire(DUInt(), Top())) self.assertTrue(DeltaGraph.check_wire(DBool(), Top())) self.assertTrue(DeltaGraph.check_wire(DTuple([int, bool]), Top())) self.assertTrue(DeltaGraph.check_wire(DUnion([int, bool]), Top())) self.assertTrue(DeltaGraph.check_wire(DArray(int, DSize(8)), Top())) self.assertTrue(DeltaGraph.check_wire(DStr(), Top())) self.assertTrue(DeltaGraph.check_wire(DRecord(RecBI), Top())) self.assertTrue(DeltaGraph.check_wire(Top(), Top())) with self.assertRaises(DeltaTypeError): DeltaGraph.check_wire(Top(), DInt()) # however it's not true if Top is used within a non-primitive type with self.assertRaises(DeltaTypeError): DeltaGraph.check_wire(DTuple([int, int]), DTuple([int, Top()])) with self.assertRaises(DeltaTypeError): DeltaGraph.check_wire(DArray(int, DSize(8)), DArray(Top(), DSize(8))) with self.assertRaises(DeltaTypeError): DeltaGraph.check_wire(DRecord(RecBI), DRecord(RecBT))
def test_DTuple(self): # primitive elements are properly handled self.check((-5, True, 3.25), DTuple([int, bool, float])) with self.assertRaises(DeltaTypeError): self.check((-5, True, 3.25), DTuple([int, bool, int])) # incapsulation self.check((-5, (1, 2)), DTuple([int, DTuple([int, int])])) with self.assertRaises(AssertionError): self.check((-5, (1, 2)), DTuple([int, DTuple([int, int])]), DTuple([int, int, int])) # mixed types self.check(([1, 2, 3], [4.0, 5.0]), DTuple([DArray(int, DSize(3)), DArray(float, DSize(2))])) self.check(("hello", "world"), DTuple([DStr(), DStr(DSize(6))])) # numpy self.check_numpy((1, 2.0, True), DTuple([int, float, bool]))
def test_DUnion(self): # primitive self.check(5, DUnion([int, bool]), DUnion([int, bool])) self.check(True, DUnion([int, bool]), DUnion([bool, int])) # compound self.check(5, DUnion([int, DTuple([int, float])])) self.check((4, 5), DUnion([int, DTuple([int, int])])) self.check((4, 5), DUnion([DArray(int, DSize(2)), DTuple([int, int])])) self.check([4, 5], DUnion([DArray(int, DSize(2)), DTuple([int, int])])) # buffer's size is always the same self.assertEqual(len(DUnion([int, bool]).pack(5)), DUnion([int, bool]).size.val) self.assertEqual(len(DUnion([int, bool]).pack(True)), DUnion([int, bool]).size.val) # numpy (throws error) with self.assertRaises( DeltaTypeError, msg="NumPy unions cannot be converted to Python types."): self.check_numpy(5, DUnion([bool, float, int]))
def test_DArray(self): """Only strict typing.""" self.assertTrue(DeltaGraph.check_wire(DArray(int, DSize(8)), DArray(int, DSize(8)))) with self.assertRaises(DeltaTypeError): DeltaGraph.check_wire(DArray(int, DSize(10)), DArray(int, DSize(8))) with self.assertRaises(DeltaTypeError): DeltaGraph.check_wire(DArray(int, DSize(8)), DArray(int, DSize(10)))
def test_DRecord(self): # primitive self.check(RecBI(True, 5), DRecord(RecBI)) self.check(-4, DInt()) self.check(RecBII(True, 5, -4), DRecord(RecBII)) # mixed self.check(RecIT(-4.0, (1, 2)), DRecord(RecIT)) self.check(RecATI([1, 2], (3.0, 4), 5), DRecord(RecATI)) self.check((RecIT(-4.0, (1, 2)), 1), DTuple([DRecord(RecIT), int])) self.check([RecIT(-4.0, (1, 2)), RecIT(5.0, (-3, -4))], DArray(DRecord(RecIT), DSize(2))) self.assertTrue(DeltaGraph.check_wire(DRaw(DRecord(RecIT)), DRaw(DRecord(RecIT))))
def command_creator(op: str, argument=0, qubit=0) -> DUInt(DSize(32)): """Helper function to create HAL commands. Parameters ---------- op : str Name of opcode. argument : int Integer representation of argument value qubit : int Integer representation of qubit address Returns ------- DUInt(DSize(32)) 32-bit HAL command """ return (Opcode[op].value << Shifts.OPCODE.value) \ | (argument << Shifts.ARG.value) | qubit
def test_str(self): """Test string representation of data types.""" # primitive self.assertEqual(str(DInt()), "DInt32") self.assertEqual(str(DInt(DSize(64))), "DInt64") self.assertEqual(str(DUInt()), "DUInt32") self.assertEqual(str(DUInt(DSize(64))), "DUInt64") self.assertEqual(str(DBool()), "DBool") self.assertEqual(str(DChar()), "DChar8") self.assertEqual(str(DFloat()), "DFloat32") self.assertEqual(str(DFloat(DSize(64))), "DFloat64") # compound self.assertEqual(str(DArray(int, DSize(8))), "[DInt32 x 8]") self.assertEqual(str(DStr()), "DStr8192") self.assertEqual(str(DStr(DSize(100))), "DStr800") self.assertEqual(str(DTuple([int, bool])), "(DInt32, DBool)") self.assertEqual(str(DRecord(RecBIS)), "{x: DBool, y: DInt32, z: DStr8192}") self.assertEqual(str(DUnion([int, bool])), "<DBool | DInt32>") # compound: DUnion self.assertEqual(str(DUnion([int])), "<DInt32>") self.assertEqual(str(DUnion([int, DUnion([int, bool])])), "<DBool | DInt32>") self.assertEqual(str(DUnion([int, DUnion([int, DUnion([int, bool])])])), "<DBool | DInt32>") # encapsulation of various types self.assertEqual(str(DUnion([int, DTuple([int, bool])])), "<(DInt32, DBool) | DInt32>") self.assertEqual(str(DArray(DTuple([int, bool]), DSize(8))), "[(DInt32, DBool) x 8]") # special self.assertEqual(str(Top()), "T") self.assertEqual(str(DSize(5)), "5") self.assertEqual(str(DSize(NamespacedName("a", "b"))), "(a.b)") self.assertEqual(str(ForkedReturn(dict(x=int, y=bool, z=str))), "ForkedReturn(x:DInt32, y:DBool, z:DStr8192)")
def test_DFloat(self): """Only 32 and 64 bits are supported.""" precision_dict = {32: -23, 64: -52} for bits, precision in precision_dict.items(): for _ in range(1000): self.check_float(random.uniform(-1, 1), DFloat(DSize(bits))) self.check(1 + 2**precision, DFloat(DSize(bits))) # Booleans and ints can be packed self.check(True, DFloat(DSize(bits))) self.check(False, DFloat(DSize(bits))) self.check(3, DFloat(DSize(bits))) # Strings and complex numbers are not packable self.assertFalse(DFloat(DSize(bits)).is_packable("abc")) self.assertFalse(DFloat(DSize(bits)).is_packable(3+5j))
def test_DRecord(self): # primitive self.check(RecBI(True, 5), DRecord(RecBI)) self.check(-4, DInt()) self.check(RecBII(True, 5, -4), DRecord(RecBII)) with self.assertRaises(DeltaTypeError): self.check(RecBI(True, 5), DRecord(RecIB)) # mixed self.check(RecIT(-4.0, (1, 2)), DRecord(RecIT)) self.check(RecATI([1, 2], (3.0, 4), 5), DRecord(RecATI)) self.check((RecIT(-4.0, (1, 2)), 1), DTuple([DRecord(RecIT), int])) self.check([RecIT(-4.0, (1, 2)), RecIT(5.0, (-3, -4))], DArray(DRecord(RecIT), DSize(2))) # numpy self.check_numpy(RecBI(False, 2), DRecord(RecBI))
def test_DComplex(self): """Only 64 and 128 bits are supported.""" for bits in (64, 128): for _ in range(1000): self.check_complex(random.uniform(-1, 1) + random.uniform(-1, 1) * 1j, DComplex(DSize(bits))) # Booleans, ints and floats can be packed self.check(True, DComplex(DSize(bits))) self.check(False, DComplex(DSize(bits))) self.check(3, DComplex(DSize(bits))) self.check(3.5, DComplex(DSize(bits))) # Strings are not packable self.assertFalse(DComplex(DSize(bits)).is_packable("abc"))
def measurement_unpacker(bitcode: DUInt(DSize(32)), qubits: Iterable) -> List: """Helper function to convert 32-bit status result from HAL into an array of measurements for given qubit indices. Parameters ---------- bitcode : DUInt(DSize(32)) 32-bit measurement status from HAL. qubits : Iterable List of qubits for which the measurement result will be returned. Returns ------- List List of measurement results for the specified qubits. Raises ------ ValueError If not all valid flags are 1. """ # split status bitcode into measurements (first 16 bits) and # valid flags (last 16 bits) measurements = bitcode & Masks.MEASUREMENTS.value valids = (bitcode & Masks.VALIDS.value) << Shifts.VALIDS.value # print('1', measurements) if valids != Masks.VALIDS.value << Shifts.VALIDS.value: raise ValueError(f"Invalid measurement!, {hex(valids)}, " f"{hex(Masks.VALIDS.value << Shifts.VALIDS.value)}") measurements_list = [] for i in qubits: measurements_list.append((measurements >> i) & 1) # print('2', measurements_list) return measurements_list
from deltalanguage.data_types import DUInt, DSize from deltalanguage.wiring import DeltaMethodBlock, NodeTemplate from ..quantum_simulators import IQuantumSimulator hal_template = NodeTemplate(name="QSim", inputs=[('hal_command', DUInt(DSize(32)))], outputs=DUInt(DSize(32))) class HardwareAbstractionLayerNode: """Encapsulates a node which receives HAL commands and uses them to perform operations on a quantum device. Parameters ---------- quantum_simulator : IQuantumSimulator Object with the IQuantumSimulator interface that accepts commands and returns measurement results. """ def __init__(self, quantum_simulator: IQuantumSimulator): self._quantum_simulator = quantum_simulator @DeltaMethodBlock(name="accept_command") def accept_command( self, hal_command: DUInt(DSize(32))) -> DUInt(DSize(32)): """Interface for ``quantum_simulator.accept_command`` that is used to create a graph node. Parameters ----------