Exemple #1
0
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()