def process_init(state):
    """Initialize a time optimization loop for a single circuit.
    Args:
    state :: ProcessState - the state encapsulating the circuit
                            to binary search on

    Returns: nothing
    """
    log_file = state.file_name + '.log'
    log_file_path = os.path.join(state.data_path, log_file)
    with open(log_file_path, "w") as log:
        # Redirect everything to a log file.
        sys.stdout = sys.stderr = log

        # Display run characteristics.
        print("PID={}\nWALL_TIME={}\nSLICE_INDEX={}\nRZ_INDEX={}\n"
              "LEARNING_RATE={}\nLEARNING_RATE_DECAY={}\n{}"
              "".format(os.getpid(), time.time(), state.slice_index,
                        state.rz_index, state.lr, state.decay,
                        state.circuit))

        # Define search space.
        max_pulse_time = get_max_pulse_time(state.circuit)
        min_steps = 0
        max_steps = int(max_pulse_time * SPN)
        print("MAX_PULSE_TIME={}\nMIN_STEPS={}\nMAX_STEPS={}"
              "".format(max_pulse_time, min_steps, max_steps))

        # Run binary search.
        binary_search_for_shortest_pulse_time(state, min_steps, max_steps)
Example #2
0
    def __init__(self, molecule, slice_index, circuit, connected_qubit_pairs):
        """See corresponding class field declarations above for other arguments.
        """
        super()
        self.molecule = molecule
        self.slice_index = slice_index
        self.circuit = circuit
        self.connected_qubit_pairs = connected_qubit_pairs
        self.unitary = get_unitary(self.circuit)
        if self.circuit.depth() > REDUCED_CIRCUIT_DEPTH_CUTOFF:
            self.pulse_time = get_max_pulse_time(
                self.circuit) * PULSE_TIME_MULTIPLIER
        else:
            self.pulse_time = get_max_pulse_time(
                self.circuit) * REDUCED_PULSE_TIME_MULTIPLIER
        self.file_name = "s{}".format(self.slice_index)
        self.data_path = os.path.join(BASE_DATA_PATH,
                                      "uccsd_{}".format(molecule.lower()))
        # TODO: We assume BASE_DATA_PATH exists.
        if not os.path.exists(self.data_path):
            os.mkdir(self.data_path)

        # Set grape parameters.
        num_qubits = self.circuit.width()
        H0 = np.zeros((NUM_STATES**num_qubits, NUM_STATES**num_qubits))
        Hops, Hnames = get_Hops_and_Hnames(num_qubits, NUM_STATES,
                                           self.connected_qubit_pairs)
        states_concerned_list = get_full_states_concerned_list(
            num_qubits, NUM_STATES)
        maxA = get_maxA(num_qubits, NUM_STATES, self.connected_qubit_pairs)
        reg_coeffs = {}
        self.grape_config = {
            "H0": H0,
            "Hops": Hops,
            "Hnames": Hnames,
            "states_concerned_list": states_concerned_list,
            "reg_coeffs": reg_coeffs,
            "maxA": maxA,
        }
        self.grape_config.update(GRAPE_TASK_CONFIG)
def main():
    # Handle CLI.
    parser = argparse.ArgumentParser()
    parser.add_argument("--slice-start",
                        type=int,
                        default=0,
                        help="the "
                        "inclusive lower bound of slice indices to include "
                        "(0-7)")
    parser.add_argument("--slice-stop",
                        type=int,
                        default=0,
                        help="the "
                        "inclusive upper bound of slice indices to include "
                        "(0-7)")
    args = vars(parser.parse_args())
    slice_start = args["slice_start"]
    slice_stop = args["slice_stop"]

    # Trim slices to only include start thru stop.
    slices = UCCSD_LIH_SLICES[slice_start:slice_stop + 1]
    slice_count = len(slices)

    # Generate the state objects to encapsulate the optimization for each slice.
    state_iter = list()
    for i, uccsdslice in enumerate(slices):
        slice_index = i + slice_start
        max_pulse_time = get_max_pulse_time(uccsdslice.circuit)
        for angle_deg in ANGLES:
            state_iter.append(
                ProcessState(uccsdslice, slice_index, angle_deg,
                             max_pulse_time * TIME_MULTIPLIER_CONSTANT))
        for k in TIME_MULTIPLIERS:
            if not k == TIME_MULTIPLIER_CONSTANT:
                state_iter.append(
                    ProcessState(uccsdslice, slice_index, ANGLE_CONSTANT,
                                 max_pulse_time * k))
    # ENDFOR

    # Run optimization on the slices.
    core_count = min(slice_count * JOBS_PER_SLICE, BROADWELL_CORE_COUNT)
    with MPIPoolExecutor(core_count) as executor:
        executor.map(process_init, state_iter)
Example #4
0
 def __init__(self, molecule, slice_index=-1):
     """See corresponding class field declarations above for other arguments.
     """
     super()
     self.molecule = molecule
     self.slice_index = slice_index
     # If the slice index is -1, the full circuit is being optimized.
     if self.slice_index == -1:
         self.circuit = UCCSD_DATA[molecule]["CIRCUIT"]
         self.file_name = "full"
     else:
         self.circuit = UCCSD_DATA[molecule]["SLICES"][slice_index].circuit
         self.file_name = "s{}".format(slice_index)
     self.unitary = get_unitary(self.circuit)
     self.grape_config = UCCSD_DATA[molecule]["GRAPE_CONFIG"]
     self.grape_config.update(GRAPE_TASK_CONFIG)
     self.pulse_time = get_max_pulse_time(self.circuit) * PULSE_TIME_MULTIPLIER
     self.data_path = os.path.join(BASE_DATA_PATH,
                                   "uccsd_{}".format(molecule.lower()))
     # TODO: We assume BASE_DATA_PATH exists.
     if not os.path.exists(self.data_path):
         os.mkdir(self.data_path)
 def __init__(self, uccsdslice, slice_index, angle_deg,
              pulse_time_multiplier, lr, decay, data_path):
     """See corresponding class field declarations for arguments.
     """
     super()
     self.uccsdslice = uccsdslice
     self.slice_index = slice_index
     self.angle = np.deg2rad(angle_deg)
     uccsdslice.update_angles([self.angle] * len(uccsdslice.angles))
     self.pulse_time_multiplier = pulse_time_multiplier
     self.pulse_time = (get_max_pulse_time(uccsdslice.circuit) *
                        pulse_time_multiplier)
     self.lr = lr
     self.decay = decay
     self.data_path = data_path
     self.log_file_name = ("s{}_a{:.2f}_t{:.2f}_l{:.4f}_d{:.4f}.log"
                           "".format(self.slice_index, self.angle,
                                     self.pulse_time, self.lr, self.decay))
     self.log_file_path = os.path.join(data_path, self.log_file_name)
     self.trial_file_name = ("s{}_a{:.2f}_t{:.2f}.json"
                             "".format(self.slice_index, self.angle,
                                       self.pulse_time))
     self.trial_file_path = os.path.join(data_path, self.trial_file_name)
Example #6
0
def process_init(uccsdslice, slice_index, angles,
                 file_names):
    """Do all necessary process specific tasks before running grape.
    Args: ugly
    Returns: nothing
    """
    # Redirect output to a log file.
    log_file = "s{}.log".format(slice_index)
    log_file_path = os.path.join(DATA_PATH, log_file)
    with open(log_file_path, "w") as log:
        # sys.stdout = sys.stderr = log

        # Display pid, time, slice id, and circuit.
        print("PID={}\nTIME={}\nSLICE_ID={}"
              "".format(os.getpid(), time.time(), slice_index))
        print(uccsdslice.circuit)
        
        # Define search space.
        # time_upper_bound is the pulse time for a trivial
        # gate lookup that we should always beat.
        time_upper_bound = get_max_pulse_time(uccsdslice.circuit)
        print("TIME_UPPER_BOUND={}".format(time_upper_bound))
        # min_steps and max_steps are the min/max steps for the
        # the search on the current angle. mid_steps is the steps
        # we will try for the current search.
        min_steps = 0
        max_steps = time_upper_bound * spn
        mid_steps = int((min_steps + max_steps) / 2)
        prev_converged_min_steps = None
        prev_converged_max_steps = None
        prev_converged_mid_steps = None
        prev_converged_sess = None
        initial_guess = None

        # We begin with no initial guess.
        grape_sess = None
        
        for i, angle in enumerate(angles):
            # Get and display necessary information, update slice angles.
            print("\nANGLE={}".format(angle))
            file_name = file_names[i]
            uccsdslice.update_angles([angle] * len(uccsdslice.angles))
            U = uccsdslice.unitary()
            search_converged = False
            # We run the first trial for the same pulse time that the
            # last angle converged to.
            if prev_converged_mid_steps is not None:
                min_steps = prev_converged_min_steps
                max_steps = prev_converged_max_steps
                mid_steps = prev_converged_mid_steps
                initial_guess = prev_converged_sess.uks
            
            # Binary search for the minimum pulse time on the current angle.
            while not search_converged:
                # Search in the search space until we have a convergence window
                # of BNS_GRANULARITY
                while min_steps + BNS_GRANULARITY < max_steps:
                    if initial_guess is not None:
                        initial_guess = resize_uks(initial_guess, mid_steps)
                    total_time = mid_steps * nps
                    print("\nMAX_STEPS={}\nMIN_STEPS={}\nMID_STEPS={}\nTIME={}"
                          "\nGRAPE_START_TIME={}"
                          "".format(max_steps, min_steps, mid_steps, total_time,
                                    time.time()))
                    grape_sess = Grape(H0, Hops, Hnames, U, total_time, mid_steps,
                                  convergence = convergence, reg_coeffs = reg_coeffs,
                                  use_gpu = use_gpu, sparse_H = sparse_H,
                                  method = method, maxA = maxA,
                                  states_concerned_list = states_concerned_list,
                                  show_plots = show_plots, file_name = file_name,
                                  data_path = DATA_PATH ,
                                  initial_guess = initial_guess)
                    print("GRAPE_END_TIME={}".format(time.time()))
                    # If the trial converged, lower the upper bound.
                    # If the tiral did not converge, raise the lower bound.
                    trial_converged = grape_sess.l <= grape_sess.conv.conv_target
                    print("TRIAL_CONVERGED={}".format(trial_converged))
                    if trial_converged:
                        search_converged = True
                        prev_converged_mid_steps = mid_steps
                        prev_converged_max_steps = max_steps
                        prev_converged_min_steps = min_steps
                        prev_converged_sess = grape_sess
                        max_steps = mid_steps
                    else:
                        min_steps = mid_steps
                    # Update mid_steps to run for the next trial.
                    mid_steps = int((max_steps + min_steps) / 2)
                # ENDWHILE
                # If binary search did not converge, then the pulse time is
                # too short and should be backed off.
                print("SEARCH_CONVERGED={}".format(search_converged))
                if not search_converged:
                    max_steps *= BACKOFF
                    mid_steps = int((max_steps + min_steps) / 2)
            # ENDWHILE
            print("CONVERGED_STEPS={}\nCONVERGED_TIME={}"
                  "".format(prev_converged_mid_steps,
                            prev_converged_mid_steps * nps))