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)
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)
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)
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))