toffoli = UnitaryMatrix(toffoli) # Start with the circuit structure circuit = Circuit(3) circuit.append_gate(VariableUnitaryGate(2), [1, 2]) circuit.append_gate(VariableUnitaryGate(2), [0, 2]) circuit.append_gate(VariableUnitaryGate(2), [1, 2]) circuit.append_gate(VariableUnitaryGate(2), [0, 2]) circuit.append_gate(VariableUnitaryGate(2), [0, 1]) # Instantiate the circuit template with qfactor circuit.instantiate( toffoli, method='qfactor', diff_tol_a=1e-12, # Stopping criteria for distance change diff_tol_r=1e-6, # Relative criteria for distance change dist_tol=1e-12, # Stopping criteria for distance max_iters=100000, # Maximum number of iterations min_iters=1000, # Minimum number of iterations slowdown_factor=0, # Larger numbers slowdown optimization # to avoid local minima ) # Calculate and print final distance dist = HilbertSchmidtCostGenerator().calc_cost(circuit, toffoli) print('Final Distance: ', dist) # If you would like to convert the unitary operations to native gates, # you should use the KAK decomposition for 2 qubit unitaries, or # qsearch or qfast for 3+ qubit unitaries.
def synthesize(self, utry: UnitaryMatrix, data: dict[str, Any]) -> Circuit: """Synthesize `utry` into a circuit, see SynthesisPass for more info.""" # 0. Skip any unitaries too small for the configured block. if self.block_size_start > utry.get_size(): _logger.warning( 'Skipping synthesis: block size is larger than input unitary.', ) return Circuit.from_unitary(utry) # 1. Create empty circuit with same size and radixes as `utry`. circuit = Circuit(utry.get_size(), utry.get_radixes()) # 2. Calculate block sizes block_size_end = utry.get_size() - 1 if self.block_size_limit is not None: block_size_end = self.block_size_limit # 3. Calculate relevant coupling_graphs # TODO: Look for topology info in `data`, use all-to-all otherwise. model = MachineModel(utry.get_size()) locations = [ model.get_locations(i) for i in range(self.block_size_start, block_size_end + 1) ] # 3. Bottom-up synthesis: build circuit up one gate at a time layer = 1 last_cost = 1.0 last_loc = None while True: remainder = utry.get_dagger() @ circuit.get_unitary() sorted_locations = self.analyze_remainder(remainder, locations) for loc in sorted_locations: # Never predict the previous location if loc == last_loc: continue _logger.info(f'Trying next predicted location {loc}.') circuit.append_gate(VariableUnitaryGate(len(loc)), loc) circuit.instantiate( utry, **self.instantiate_options, # type: ignore ) cost = self.cost.calc_cost(circuit, utry) _logger.info(f'Instantiated; layers: {layer}, cost: {cost:e}.') if cost < self.success_threshold: _logger.info(f'Circuit found with cost: {cost:e}.') _logger.info('Successful synthesis.') return circuit progress_threshold = self.progress_threshold_a progress_threshold += self.progress_threshold_r * np.abs(cost) if last_cost - cost >= progress_threshold: _logger.info('Progress has been made, depth increasing.') last_loc = loc last_cost = cost layer += 1 break _logger.info('Progress has not been made.') circuit.pop((-1, loc[0]))