def test_get_unitary(gate: Gate) -> None: size = gate.get_size() circ = Circ(size, radixes=gate.get_radixes()) circ.append_gate(gate, list(range(size))) num_params = circ.get_num_params() x = np.random.random((num_params, )) circuit = Circuit(circ) assert np.allclose(circ.get_unitary(x).get_numpy(), circuit.get_unitary(x))
def test_value(self, gate: Gate) -> None: circuit = Circuit(gate.get_size(), gate.get_radixes()) assert circuit.is_differentiable() circuit.append_gate(gate, list(range(gate.get_size()))) if isinstance(gate, DifferentiableUnitary): assert circuit.is_differentiable() else: assert not circuit.is_differentiable()
def test_get_grad(gate: Gate) -> None: size = gate.get_size() circ = Circ(size, radixes=gate.get_radixes()) circ.append_gate(gate, list(range(size))) num_params = circ.get_num_params() x = np.random.random((num_params, )) circuit = Circuit(circ) grad_python = circ.get_grad(x) grad_rust = circuit.get_grad(x) for py, rs in zip(grad_python, grad_rust): assert np.allclose(py, rs)
def test_radix_mismatch_2(self, qutrit_gate: Gate) -> None: circuit = Circuit(qutrit_gate.get_size()) location = list(range(qutrit_gate.get_size())) params = [0] * qutrit_gate.get_num_params() op = Operation(qutrit_gate, location, params) try: circuit.check_valid_operation(op) except ValueError: return except BaseException: assert False, 'Unexpected Exception' assert False
def test_get_unitary_and_grad(gate: Gate) -> None: size = gate.get_size() circ = Circ(size, radixes=gate.get_radixes()) circ.append_gate(gate, list(range(size))) num_params = circ.get_num_params() x = np.random.random((num_params, )) circuit = Circuit(circ) utry_python, grad_python = circ.get_unitary_and_grad(x) utry_rust, grad_rust = circuit.get_unitary_and_grad(x) assert np.allclose(utry_python.get_numpy(), utry_rust) for i, (py, rs) in enumerate(zip(grad_python, grad_rust)): assert np.allclose(py, rs)
def test_random_circuit_qubit_gates(qubit_gate: Gate) -> None: circ = Circ(qubit_gate.get_size()) circ.append_gate(qubit_gate, location=list(range(qubit_gate.get_size()))) num_params = circ.get_num_params() if qubit_gate.is_constant(): assert num_params == 0 x = np.random.random((num_params, )) circuit = Circuit(circ) py = circ.get_unitary(x).get_numpy() rs = circuit.get_unitary(x) assert py.shape == rs.shape assert py.dtype is rs.dtype np.testing.assert_allclose(py, rs, verbose=True)
def __init__(self, gate: Gate, frozen_params: dict[int, float]) -> None: """ Create a gate which fixes some of the parameters it takes. Args: gate (Gate): The Gate to fix the parameters of. frozen_params (dict[int, float]): A dictionary mapping parameters indices to the fixed value they should be. Raises: ValueError: If any of the `frozen_params` indices are greater than the number of parameters `gate` takes or less than 0 or if the total amount of `frozen_params` is larger than the number of parameters `gate` takes. """ if not isinstance(gate, Gate): raise TypeError('Expected gate, got %s.' % type(gate)) if not isinstance(frozen_params, dict): raise TypeError( 'Expected dict for frozen_params, ' 'got %s.' % type(frozen_params), ) if not len(frozen_params) <= gate.get_num_params(): raise ValueError( 'Too many fixed parameters specified, expected at most' ' %d, got %d' % (gate.get_num_params(), len(frozen_params)), ) keys = list(frozen_params.keys()) values = list(frozen_params.values()) if not all(isinstance(p, int) for p in keys): fail_idx = [isinstance(p, int) for p in keys].index(False) raise TypeError( 'Expected frozen_params keys to be int, got %s.' % type(keys[fail_idx]), ) if not all(isinstance(p, (int, float)) for p in values): typechecks = [isinstance(p, (int, float)) for p in values] fail_idx = typechecks.index(False) raise TypeError( 'Expected frozen_params values to be float, got %s.' % type(values[fail_idx]), ) if not all(0 <= p < gate.get_num_params() for p in keys): fail_idx = [0 <= p < gate.get_num_params() for p in keys].index(False) raise ValueError( 'Expected parameter index to be non-negative integer' ' < %d, got %d.' % (gate.get_num_params(), keys[fail_idx]), ) self.gate = gate self.num_params = gate.get_num_params() - len(frozen_params) self.size = gate.get_size() self.radixes = gate.get_radixes() self.frozen_params = frozen_params self.unfixed_param_idxs = [ i for i in range(gate.get_num_params()) if i not in self.frozen_params.keys() ]
def __init__(self, gate: Gate, tag: Any) -> None: """Associate `tag` with `gate`.""" if not isinstance(gate, Gate): raise TypeError('Expected gate object, got %s' % type(gate)) self.gate = gate self.tag = tag self.name = 'Tagged(%s:%s)' % (gate.get_name(), tag) self.num_params = gate.get_num_params() self.size = gate.get_size() self.radixes = gate.get_radixes() # If input is a constant gate, we can cache the unitary. if self.num_params == 0: self.utry = gate.get_unitary()
def __init__( self, gate: Gate, location: CircuitLocationLike, params: Sequence[float] = [], ) -> None: """ Operation Constructor. s Args: gate (Gate): The cell's gate. location (CircuitLocationLike): The set of qudits this gate affects. params (Sequence[float]): The parameters for the gate. Raises: ValueError: If `gate`'s size doesn't match `location`'s length. ValueError: If `gate`'s size doesn't match `params`'s length. """ if not isinstance(gate, Gate): raise TypeError('Expected gate, got %s.' % type(gate)) if not CircuitLocation.is_location(location): raise TypeError('Invalid location.') location = CircuitLocation(location) if len(location) != gate.get_size(): raise ValueError('Gate and location size mismatch.') self.num_params = gate.get_num_params() self.radixes = gate.get_radixes() self.size = gate.get_size() if len(params) == 0 and self.get_num_params() != 0: params = [0.0] * self.get_num_params() self.check_parameters(params) self._gate = gate self._location = location self._params = list(params)
def test_get_qasm_gate_def(self, gate: Gate) -> None: try: qasm_gate_def = gate.get_qasm_gate_def() except AttributeError: return except BaseException: assert False, 'Unexpected error on gate.get_qasm_gate_def() call.' assert isinstance(qasm_gate_def, str)
def __init__( self, gate: Gate, num_controls: int = 1, radixes: Sequence[int] = [], ) -> None: """Construct a ControlledGate.""" if not isinstance(gate, Gate): raise TypeError('Expected gate object, got %s.' % type(gate)) if not is_integer(num_controls): raise TypeError( 'Expected integer for num_controls, got %s.' % type(num_controls), ) if num_controls < 1: raise ValueError( 'Expected positive integer for num_controls, got %d.' % num_controls, ) if len(radixes) != 0 and not is_valid_radixes(radixes, num_controls): raise TypeError('Invalid radixes.') self.gate = gate self.size = gate.get_size() + num_controls self.num_controls = num_controls self.radixes = tuple(radixes or [2] * self.size) + gate.get_radixes() self.name = '%d-Controlled(%s)' % (num_controls, gate.get_name()) self.num_params = gate.get_num_params() self.Ic = np.identity(2 ** num_controls) # TODO: General radix support self.It = np.identity(gate.get_dim()) self.OneProj = np.zeros(self.Ic.shape) self.OneProj[-1, -1] = 1 self.left = np.kron((self.Ic - self.OneProj), self.It) # If input is a constant gate, we can cache the unitary. if self.num_params == 0: U = self.gate.get_unitary() right = np.kron(self.OneProj, U) self.utry = UnitaryMatrix(self.left + right, self.get_radixes())
def test_is_parameterized_gate(self, param_gate: Gate) -> None: assert not param_gate.is_constant() assert param_gate.is_parameterized()
def test_valid_1(self, gate: Gate) -> None: circuit = Circuit(gate.get_size(), gate.get_radixes()) location = list(range(gate.get_size())) params = [0] * gate.get_num_params() circuit.check_valid_operation(Operation(gate, location, params))
def test_get_unitary(self, gate: Gate) -> None: params = np.random.rand(gate.get_num_params()) utry = gate.get_unitary(params) assert isinstance(utry, UnitaryMatrix)
def test_unitary_dim_match(self, gate: Gate) -> None: params = np.random.rand(gate.get_num_params()) utry = gate.get_unitary(params) assert utry.get_shape() == (gate.get_dim(), gate.get_dim())
def test_with_frozen_params(self, gate: Gate) -> None: num_params = gate.get_num_params() frozen_params = {i: float(j) for i, j in enumerate(range(num_params))} frozen_gate = gate.with_frozen_params(frozen_params) assert isinstance(frozen_gate, FrozenParameterGate) assert frozen_gate.frozen_params == frozen_params
def test_check_parameters_valid(self, gate: Gate) -> None: gate.check_parameters(np.random.rand(gate.get_num_params())) gate.check_parameters([0] * gate.get_num_params())
def test_get_radixes_qutrit(self, qutrit_gate: Gate) -> None: assert all(radix == 3 for radix in qutrit_gate.get_radixes())
def test_is_constant_parameterized(self, gate: Gate) -> None: assert gate.is_constant() or gate.is_parameterized()
def test_get_num_params_parameterized(self, param_gate: Gate) -> None: assert param_gate.get_num_params() != 0
def test_get_radixes(self, gate: Gate) -> None: assert isinstance(gate.get_radixes(), tuple) assert all(isinstance(radix, int) for radix in gate.get_radixes()) assert all(radix > 0 for radix in gate.get_radixes())
def test_get_num_params(self, gate: Gate) -> None: assert isinstance(gate.get_num_params(), int) assert gate.get_num_params() >= 0
def test_repr(self, gate: Gate) -> None: assert isinstance(gate.__repr__(), str)
def test_with_all_frozen_params(self, gate: Gate) -> None: frozen_gate = gate.with_all_frozen_params([0] * gate.get_num_params()) assert isinstance(frozen_gate, FrozenParameterGate) frozen_params = {i: 0 for i in range(gate.get_num_params())} assert frozen_gate.frozen_params == frozen_params
def test_valid_2(self, gate: Gate) -> None: circuit = Circuit(gate.get_size() + 2, (2, 2) + gate.get_radixes()) location = [x + 2 for x in list(range(gate.get_size()))] params = [0] * gate.get_num_params() circuit.check_valid_operation(Operation(gate, location, params))
def test_gate_size_matches_radixes(self, gate: Gate) -> None: assert len(gate.get_radixes()) == gate.get_size()
def __init__( self, gate: Gate, locations: Sequence[CircuitLocationLike], radixes: Sequence[int], ) -> None: """ Create a gate that has parameterized location. Args: gate (Gate): The gate to parameterize location for. locations (Sequence[CircuitLocationLike]): A sequence of locations. Each location represents a valid placement for gate. radixes (Sequence[int]): The number of orthogonal states for each qudit. Defaults to qubits. Raises: ValueError: If there are not enough locations or the locations are incorrectly sized. Notes: The locations are calculated in their own space and are not relative to a circuit. This means you should consider the VariableLocationGate as its own circuit when deciding the locations. For example, if you want to multiplex the (2, 3) and (3, 5) placements of a CNOT on a 6-qubit circuit, then you would give the VariableLocationGate the (0, 1) and (1, 2) locations and place the VariableLocationGate on qubits (2, 3, 5) on the circuit. """ if not isinstance(gate, Gate): raise TypeError('Expected gate object, got %s' % type(gate)) if not all(CircuitLocation.is_location(l) for l in locations): raise TypeError('Expected a sequence of valid locations.') locations = [CircuitLocation(l) for l in locations] if not all(len(l) == gate.get_size() for l in locations): raise ValueError('Invalid sized location.') if len(locations) < 1: raise ValueError('VLGs require at least 1 locations.') self.gate = gate self.name = 'VariableLocationGate(%s)' % gate.get_name() self.locations = list(locations) if radixes is None: # Calculate radixes radix_map: dict[int, int | None] = {i: None for i in range(self.size)} for l in locations: for radix, qudit_index in zip(gate.get_radixes(), l): if radix_map[qudit_index] is None: radix_map[qudit_index] = radix elif radix_map[qudit_index] != radix: raise ValueError( 'Gate cannot be applied to all locations' ' due to radix mismatch.', ) self.radixes = tuple(radix_map.values()) else: for l in locations: for radix, qudit_index in zip(gate.get_radixes(), l): if radixes[qudit_index] != radix: raise ValueError( 'Gate cannot be applied to all locations' ' due to radix mismatch.', ) self.radixes = tuple(radixes) self.size = len(self.radixes) self.num_params = self.gate.get_num_params() + len(locations) self.extension_size = self.size - self.gate.get_size() # TODO: This needs to changed for radixes self.I = np.identity(2**self.extension_size) self.perms = np.array([ PermutationMatrix.from_qubit_location(self.size, l) for l in self.locations ])
def test_get_dim(self, gate: Gate) -> None: assert isinstance(gate.get_dim(), int) assert gate.get_dim() > 0
def test_get_num_params_constant(self, constant_gate: Gate) -> None: assert constant_gate.get_num_params() == 0
def test_check_parameters_invalid(self, gate: Gate) -> None: with pytest.raises(TypeError): gate.check_parameters('a') # type: ignore with pytest.raises(TypeError): gate.check_parameters(1) # type: ignore if gate.is_parameterized(): with pytest.raises(TypeError): error_list = ['a'] * gate.get_num_params() gate.check_parameters(error_list) # type: ignore with pytest.raises(ValueError): gate.check_parameters(np.random.rand(gate.get_num_params() + 1))