def _get_optima(in_task: ProblemGenerationTaskT, problem_generation_base_dir, p_max: int = 5) \ -> Dict[int, OptimizationResult]: """Helper function to get optimal parameters for a given instance of a given device's problem, subselected on a given number of qubits. This function is annotated with lru_cache so you can call it once for each p-value without re-doing the (expensive) optimization. optimize_instance_interp_heuristic uses low-p-values to bootstrap guesses for high-p-values, so it just does the optimization for everything up to p_max. This is the meat of `generate_problems` to get the optimal parameters for a given (device, instance, n_qubit, p) specification which are all the relevant parameters. """ data = recirq.load(in_task, base_dir=problem_generation_base_dir) problem = data['problem'] if isinstance(problem, HardwareGridProblem): param_guess = [np.pi / 8, -np.pi / 8] elif isinstance(problem, ThreeRegularProblem): param_guess = [np.pi / 8, -np.pi / 8] elif isinstance(problem, SKProblem): n = problem.graph.number_of_nodes() param_guess = [ np.arccos(np.sqrt((1 + np.sqrt((n - 2) / (n - 1))) / 2)), -np.pi / 8 ] else: raise ValueError("Unknown problem type: {}".format(problem)) optima = optimize_instance_interp_heuristic( graph=problem.graph, p_max=p_max, param_guess_at_p1=param_guess, verbose=True, ) return {op.p: op for op in optima}
def collect_optimization_data( task: OptimizationTask, base_dir: str = DEFAULT_BASE_DIR, problem_generation_base_dir: str = DEFAULT_PROBLEM_GENERATION_BASE_DIR, precomputation_base_dir: str = DEFAULT_PRECOMPUTATION_BASE_DIR ) -> None: """Collect data for a QAOA optimization experiment. Args: task: The optimization task. base_dir: The base directory in which to save data. problem_generation_base_dir: The base directory of the problem. """ if recirq.exists(task, base_dir=base_dir): print(f'{task.fn} already exists. Skipping.') return sampler = recirq.get_sampler_by_name(device_name=task.device_name) problem = recirq.load( task.generation_task, base_dir=problem_generation_base_dir)['problem'] # type: ProblemT lowest_energy_found = np.inf best_bitstring_found = None evaluated_points = [] bitstring_lists = [] energy_lists = [] mean_energies = [] def f(x): print('Evaluating objective ...') # Create circuit gammas = x[:task.p] betas = x[task.p:] initial_qubits, circuit, final_qubits = _get_circuit( problem, gammas, betas, task.device_name) # Sample bitstrings from circuit result = sampler.run(program=circuit, repetitions=task.algorithm.n_shots) # Process bitstrings nonlocal lowest_energy_found nonlocal best_bitstring_found bitstrings = result.measurements['z'] initial_indices = {q: i for i, q in enumerate(initial_qubits)} final_indices = [initial_indices[q] for q in final_qubits] nodelist = np.argsort(final_indices) energies = hamiltonian_objectives(bitstrings, problem.graph, nodelist=nodelist) lowest_energy_index = np.argmin(energies) lowest_energy = energies[lowest_energy_index] if lowest_energy < lowest_energy_found: lowest_energy_found = lowest_energy best_bitstring_found = bitstrings[lowest_energy_index] mean = np.mean(energies) # Save bitstrings and other data evaluated_points.append(recirq.NumpyArray(x)) bitstring_lists.append(recirq.BitArray(bitstrings)) energy_lists.append(recirq.NumpyArray(np.array(energies))) mean_energies.append(mean) print('Objective function: {}'.format(mean)) print() return mean result = recirq.optimize.minimize(f, task.x0, method=task.algorithm.method, **(task.algorithm.options or {})) result_data = { 'x': result.x, 'fun': result.fun, 'nit': result.nit, 'nfev': result.nfev, 'lowest_energy_found': lowest_energy_found, 'best_bitstring_found': best_bitstring_found, 'evaluated_points': evaluated_points, 'bitstring_lists': bitstring_lists, 'energy_lists': energy_lists, 'mean_energies': mean_energies } if hasattr(result, 'x_iters'): result_data['x_iters'] = result['x_iters'] if hasattr(result, 'func_vals'): result_data['func_vals'] = result['func_vals'] if hasattr(result, 'model_vals'): result_data['model_vals'] = result['model_vals'] # Evaluate at optimal angles if they have been precomputed angle_precomputation_task = AnglePrecomputationTask( dataset_id=task.dataset_id, generation_task=task.generation_task, p=task.p) if recirq.exists(angle_precomputation_task, base_dir=precomputation_base_dir): optimum = recirq.load(angle_precomputation_task, base_dir=precomputation_base_dir)['optimum'] gammas = optimum.gammas betas = optimum.betas initial_qubits, circuit, final_qubits = _get_circuit( problem, gammas, betas, task.device_name) result = sampler.run(program=circuit, repetitions=50_000) bitstrings = result.measurements['z'] initial_indices = {q: i for i, q in enumerate(initial_qubits)} final_indices = [initial_indices[q] for q in final_qubits] nodelist = np.argsort(final_indices) energies = hamiltonian_objectives(bitstrings, problem.graph, nodelist=nodelist) mean = np.mean(energies) result_data['optimal_angles'] = gammas + betas result_data['optimal_angles_fun'] = mean recirq.save(task=task, data=result_data, base_dir=base_dir)
async def collect_data(task: PrecomputedDataCollectionTask, base_dir=None, problem_generation_base_dir=None, precomputation_base_dir=None, ): """Collect and save data for the experiment specified by params. The associated problem generation data must already exist. """ if base_dir is None: base_dir = DEFAULT_BASE_DIR if problem_generation_base_dir is None: problem_generation_base_dir = DEFAULT_PROBLEM_GENERATION_BASE_DIR if precomputation_base_dir is None: precomputation_base_dir = DEFAULT_PRECOMPUTATION_BASE_DIR if recirq.exists(task, base_dir=base_dir): print(f"{task.fn} already exists. Skipping.") return precompute_task = task.precomputation_task generation_task = precompute_task.generation_task problem = recirq.load(generation_task, base_dir=problem_generation_base_dir)[ 'problem'] # type: ProblemT optimum = recirq.load(precompute_task, base_dir=precomputation_base_dir)[ 'optimum'] # type: OptimizationResult sampler = recirq.get_sampler_by_name(device_name=task.device_name) device = recirq.get_device_obj_by_name(device_name=task.device_name) try: if isinstance(problem, HardwareGridProblem): initial_qubits = [cirq.GridQubit(r, c) for r, c in problem.coordinates] circuit, final_qubits = get_compiled_hardware_grid_circuit( problem=problem, qubits=initial_qubits, gammas=optimum.gammas, betas=optimum.betas, non_negligible=False) elif isinstance(problem, SKProblem): initial_qubits = place_line_on_device( device_name=task.device_name, n=problem.graph.number_of_nodes(), line_placement_strategy='mixed') circuit, final_qubits = get_compiled_sk_model_circuit( problem=problem, qubits=initial_qubits, gammas=optimum.gammas, betas=optimum.betas, non_negligible=False) elif isinstance(problem, ThreeRegularProblem): initial_qubits, circuit, final_qubits = get_compiled_3_regular_maxcut_circuit( problem=problem, device=device, gammas=optimum.gammas, betas=optimum.betas) else: raise ValueError("Unknown problem: {}".format(problem)) except BadlyStructuredCircuitError: print("!!!! Badly structured circuit: {}".format(task)) # TODO https://github.com/quantumlib/Cirq/issues/2553 return if not task.structured: # Left align circuit = compile_to_non_negligible(circuit) circuit = cirq.Circuit(circuit.all_operations()) if task.echoed: assert task.structured raise NotImplementedError("To be implemented in follow-up PR") violation_indices = find_circuit_structure_violations(circuit) circuit.program_id = task.fn result = await sampler.run_async(program=circuit, repetitions=task.n_shots) bitstrings = result.measurements['z'] recirq.save(task=task, data={ 'bitstrings': recirq.BitArray(bitstrings), 'qubits': initial_qubits, 'final_qubits': final_qubits, 'circuit': circuit, 'violation_indices': violation_indices, }, base_dir=base_dir) print(f"{task.fn} complete.")
async def collect_p1_landscape_data( task: P1LandscapeDataCollectionTask, base_dir=None, problem_generation_base_dir=None) -> None: if base_dir is None: base_dir = DEFAULT_BASE_DIR if problem_generation_base_dir is None: problem_generation_base_dir = DEFAULT_PROBLEM_GENERATION_BASE_DIR if recirq.exists(task, base_dir=base_dir): print(f"{task.fn} already exists. Skipping.") return generation_task = task.generation_task problem = recirq.load(generation_task, base_dir=problem_generation_base_dir)[ 'problem'] # type: ProblemT sampler = recirq.get_sampler_by_name(device_name=task.device_name) device = recirq.get_device_obj_by_name(device_name=task.device_name) if isinstance(problem, HardwareGridProblem): initial_qubits = [cirq.GridQubit(r, c) for r, c in problem.coordinates] circuit, final_qubits = get_compiled_hardware_grid_circuit( problem=problem, qubits=initial_qubits, gammas=[task.gamma], betas=[task.beta], non_negligible=False) elif isinstance(problem, SKProblem): initial_qubits = place_line_on_device( device_name=task.device_name, n=problem.graph.number_of_nodes(), line_placement_strategy='mixed') circuit, final_qubits = get_compiled_sk_model_circuit( problem=problem, qubits=initial_qubits, gammas=[task.gamma], betas=[task.beta], non_negligible=False) elif isinstance(problem, ThreeRegularProblem): initial_qubits, circuit, final_qubits = get_compiled_3_regular_maxcut_circuit( problem=problem, device=device, gammas=[task.gamma], betas=[task.beta]) else: raise ValueError("Unknown problem: {}".format(problem)) flipped_circuit = circuit.copy() measurement_op = flipped_circuit[-1].operations[0] flipped_circuit = flipped_circuit[:-1] flipped_circuit.append(cirq.Moment(cirq.X.on_each(*measurement_op.qubits))) flipped_circuit.append( measurement_op.gate.with_bits_flipped(*range(problem.graph.number_of_nodes())).on( *measurement_op.qubits)) unmodified_n_shots = task.n_shots // 2 flipped_n_shots = task.n_shots - unmodified_n_shots t0 = timeit.default_timer() circuit.program_id = task.fn unmodified_result = await sampler.run_async(program=circuit, repetitions=unmodified_n_shots) circuit.program_id = task.fn + '-flip' flipped_result = await sampler.run_async(program=flipped_circuit, repetitions=flipped_n_shots) t1 = timeit.default_timer() result = unmodified_result + flipped_result execution_time = t1 - t0 print(f'Circuit execution time: {execution_time} s') t0 = timeit.default_timer() bitstrings = result.measurements['z'] recirq.save(task=task, data={ 'bitstrings': recirq.BitArray(bitstrings), 'qubits': initial_qubits, 'final_qubits': final_qubits, 'execution_time': execution_time }, base_dir=base_dir) t1 = timeit.default_timer() print(f'Time to save bitstrings: {t1 - t0} s') print()