def _get_cccu_commands( control_mapping: _ControlQubits, rsrc: ResourceAllocator, max_num_qubits: int) -> Tuple[_ControlQubits, List[ICommand]]: """ Builds a circuit that computes the AND condition on the `control_mapping` qubits (some possibly negated) into one qubit. To acheive this, when the number of control bits is greater than `max_num_qubits`, an `ancilla_allocator` may be used to allocate extra qubits. The resulting computation consists of X gates (for negation) and CCX gates to construct a computation tree. This tree has a logarithmic depth. Returns a tuple consisting of a new control qubits dict and the computation commands. """ commands: List[ICommand] = [] for (qubit_id, is_positive) in control_mapping.items(): if is_positive == 0: commands.append(GateCmd(X_GATE, qubit_id)) control_qubit_ids = list(control_mapping) while len(control_qubit_ids) > max_num_qubits: q1 = control_qubit_ids.pop(0) q2 = control_qubit_ids.pop(0) q3 = rsrc.allocate_qubit() commands.append(GateCmd(X_GATE, q3, control_qubit_ids={q1, q2})) control_qubit_ids.append(q3) return {q: 1 for q in control_qubit_ids}, commands
def _test_one_qubit_command_3(self, cmd_class) -> None: min_unused_qubit_id = 2 cmd_1: ICommand = GateCmd(cmd_class, 0) cmd_2: ICommand = GateCmd(cmd_class, 1) commands: List[ICommand] = [cmd_1,cmd_2] actual: List[ICommand] = QuasarOpt().run(commands, min_unused_qubit_id) expected: List[ICommand] = [cmd_1, cmd_2] self.assertListEqual(actual, expected)
def _test_two_qubits_cu3_command_2(self) -> None: min_unused_qubit_id = 2 cmd_1: ICommand = GateCmd(U3_GATE, 1, params=[2.2, 3.3, 4.4], control_qubit_ids={0}) cmd_2: ICommand = GateCmd(U3_GATE, 1, params=[2.2, 3.3, 4.4], control_qubit_ids={0}) commands: List[ICommand] = [cmd_1, cmd_2] actual: List[ICommand] = QuasarOpt().run(commands, min_unused_qubit_id) expected: List[ICommand] = [cmd_1, cmd_2] self.assertListEqual(actual, expected)
def _test_one_qubit_u3_command_2(self) -> None: min_unused_qubit_id = 1 cmd_1: ICommand = GateCmd(U3_GATE, 0, params=[1.1, 2.2, 3.3]) cmd_2: ICommand = GateCmd(U3_GATE, 0, params=[1.1, 3.3, 2.2]) commands: List[ICommand] = [cmd_1, cmd_2] actual: List[ICommand] = QuasarOpt().run(commands, min_unused_qubit_id) expected: List[ICommand] = [cmd_1, cmd_2] self.assertListEqual(actual, expected)
def _test_three_qubits_command_5(self, cmd_class) -> None: min_unused_qubit_id = 5 cmd_1: ICommand = GateCmd(cmd_class, 2, control_qubit_ids={0, 1}) cmd_2: ICommand = GateCmd(cmd_class, 0, control_qubit_ids={2, 1}) commands: List[ICommand] = [cmd_1, cmd_2] actual: List[ICommand] = QuasarOpt().run(commands, min_unused_qubit_id) expected: List[ICommand] = [cmd_1, cmd_2] self.assertListEqual(actual, expected)
def _test_three_qubits_reset_command_3(self, cmd_class_1, cmd_class_2) -> None: min_unused_qubit_id = 3 cmd_1: ICommand = GateCmd(cmd_class_1, 2, control_qubit_ids={0, 1}) cmd_2: ICommand = cmd_class_2(2) cmd_3: ICommand = GateCmd(cmd_class_1, 2, control_qubit_ids={0, 1}) commands: List[ICommand] = [cmd_1, cmd_2, cmd_3] actual: List[ICommand] = QuasarOpt().run(commands, min_unused_qubit_id) expected: List[ICommand] = [cmd_1, cmd_2, cmd_3] self.assertListEqual(actual, expected)
def _test_two_qubits_command_6(self, cmd_class) -> None: min_unused_qubit_id = 4 cmd_1: ICommand = GateCmd(cmd_class, 1, control_qubit_ids={0}) cmd_2: ICommand = GateCmd(cmd_class, 3, control_qubit_ids={2}) cmd_3: ICommand = GateCmd(cmd_class, 1, control_qubit_ids={2}) commands: List[ICommand] = [cmd_1, cmd_2, cmd_3] actual: List[ICommand] = QuasarOpt().run(commands, min_unused_qubit_id) expected: List[ICommand] = [cmd_1, cmd_2, cmd_3] self.assertListEqual(actual, expected)
def _test_one_qubit_commands_2(self, cmd_class_1, cmd_class_2) -> None: min_unused_qubit_id = 1 cmd_1: ICommand = GateCmd(cmd_class_1, 0) cmd_2: ICommand = GateCmd(cmd_class_2, 0) cmd_3: ICommand = GateCmd(cmd_class_2, 0) cmd_4: ICommand = GateCmd(cmd_class_1, 0) commands: List[ICommand] = [cmd_1, cmd_2, cmd_3, cmd_4] actual: List[ICommand] = QuasarOpt().run(commands, min_unused_qubit_id) expected: List[ICommand] = [] self.assertListEqual(actual, expected)
def _test_two_qubits_commands_3(self, cmd_class_1, cmd_class_2) -> None: min_unused_qubit_id = 2 cmd_1: ICommand = GateCmd(cmd_class_1, 1, control_qubit_ids={0}) cmd_2: ICommand = GateCmd(cmd_class_2, 0, control_qubit_ids={1}) cmd_3: ICommand = GateCmd(cmd_class_2, 1, control_qubit_ids={0}) cmd_4: ICommand = GateCmd(cmd_class_1, 0, control_qubit_ids={1}) commands: List[ICommand] = [cmd_1, cmd_2, cmd_3, cmd_4] actual: List[ICommand] = QuasarOpt().run(commands, min_unused_qubit_id) expected: List[ICommand] = [cmd_1, cmd_2, cmd_3, cmd_4] self.assertListEqual(actual, expected)
def inverse_command(command: ICommand): if not isinstance(command, GateCmd): raise ValueError(f"Inverse of {type(command)} is impossible.") inv_gate, inv_params = invert_gate(command._gate, command._params) return GateCmd(inv_gate, command.get_target_qubit_id(), command.get_control_qubit_ids(), inv_params)
def _get_on_gate_commands(self, node: GateNode) -> List[ICommand]: if not self._control_mapping: return [ GateCmd(node.gate, node.get_target_qubit_id(), params=node.params) ] negate_negative_commands: List[ICommand] = [] for qubit_id, mask in self._control_mapping.items(): if mask == 0: negate_negative_commands.append(GateCmd(X_GATE, qubit_id)) num_ancillas = 0 commands: List[ICommand] control_commands: List[ICommand] = [] control_qubit_ids = list(self._control_mapping) max_controls = 2 if node.gate == X_GATE else 1 # TODO(adsz): to be defined by gate / target architecture while len(control_qubit_ids) > max_controls: control_1 = control_qubit_ids.pop(0) control_2 = control_qubit_ids.pop(0) ancilla = self._rsrc.allocate_qubit() num_ancillas += 1 control_qubit_ids.append(ancilla) control_commands.append( GateCmd(X_GATE, ancilla, control_qubit_ids={control_1, control_2})) controlled_command = GateCmd(node.gate, node.get_target_qubit_id(), params=node.params, control_qubit_ids=set(control_qubit_ids)) commands = (control_commands + [controlled_command] + self._inversed(control_commands)) self._rsrc.free_qubits(num_ancillas) return (negate_negative_commands + commands + self._inversed(negate_negative_commands))
def _get_qubit_negation_commands(control_mapping) -> List[ICommand]: """ This function makes sure that all control qubits are in fact positively controlling. """ result: List[ICommand] = [] for qubit_id, mask in control_mapping.items(): if mask == 0: result.append(GateCmd(X_GATE, qubit_id)) control_mapping[qubit_id] = 1 return result
def check_commutation(cmd1: GateCmd, cmd2: GateCmd) -> bool: """ Returns True if two gates can be swapped with no change to the outcome. """ # TODO(adsz): In future, it might be also beneficial to check commutation # that alters the gates (with similar gate complexity). if cmd1 == cmd2: return True if not ((cmd1.get_control_qubit_ids() | {cmd1.get_target_qubit_id()}) & (cmd2.get_control_qubit_ids() | {cmd2.get_target_qubit_id()})): return True if cmd1.gate == Z_GATE and cmd2.gate == Z_GATE: return True if cmd1.gate == X_GATE and cmd2.gate == X_GATE: if cmd1.get_target_qubit_id() in cmd2.get_control_qubit_ids(): return False elif cmd2.get_target_qubit_id() in cmd1.get_control_qubit_ids(): return False else: return True # TODO(adsz): Two simplest rules as a starting point. Add more rules. return False
def on_if_flip(self, if_flip: IfFlipNode) -> None: qubits_counter = self._rsrc.get_min_unused_qubit_id() cvis = CompileVisitor(self._rsrc) if_flip.get_condition().accept(cvis) cvis._commands.extend( CompileVisitor._get_reduced_commands(2, cvis._control_mapping, cvis._rsrc)) cvis._commands.extend( CompileVisitor._get_qubit_negation_commands(cvis._control_mapping)) control_qubit_ids = list(cvis._control_mapping) if_commands: List[ICommand] = cvis._commands flip_command = GateCmd(Z_GATE, control_qubit_ids[-1], control_qubit_ids=set(control_qubit_ids[:-1])) self._commands.extend(if_commands + [flip_command] + self._inversed(if_commands)) num_ancillas = self._rsrc.get_min_unused_qubit_id() - qubits_counter self._rsrc.free_qubits(num_ancillas)