Skip to content

Optimizer

Leon edited this page Nov 10, 2024 · 2 revisions

Author: https://github.com/Supercabb
License: MIT
LILY QML 2024 - Leon Kaiser

The Optimizer class is designed to handle the optimization process within the LILY-QML project. It manages the initialization of qubits, loading of data, parsing and execution of jobs, and interaction with optimizer algorithms. The class ensures that data structures are correctly formatted and that the optimization process runs smoothly.

Overview

  • Purpose: Coordinate the optimization of quantum circuits by interfacing with optimizer algorithms and managing qubit states.
  • Key Responsibilities:
    • Load and validate optimizer modules.
    • Initialize qubit objects.
    • Parse and evaluate job strings.
    • Execute optimization steps.
    • Update training matrices based on optimization results.

Attributes

  • Qubit_Object (dict): A dictionary mapping qubit indices to Qubit instances.

    self.Qubit_Object = {0: Qubit(0), 1: Qubit(1), ...}
  • current_job (str): The job string currently being processed.

    self.current_job = "(1.0, 2.0, 3.0) Qubit_0 (1:200; 0:50) (S:1)"
  • optimizer (str): The name of the optimizer class to be used.

    self.optimizer = "AdaGradOptimizer"
  • optimizer_class (class): The optimizer class imported dynamically based on optimizer.

    self.optimizer_class = <class 'AdaGradOptimizer'>
  • config_path (str): Path to the configuration directory.

    self.config_path = "var"
  • dict_params_current_job (dict): A dictionary containing parsed parameters from the current_job string.

    self.dict_params_current_job = {
        "matrix_row_str": "1.0, 2.0, 3.0",
        "num_qubit": "0",
        "dist_0_0": "1",
        "vdist_0_1": "200",
        "dist_1_0": "0",
        "dist_1_1": "50",
        "state": "1",
        "matrix_elements": [1.0, 2.0, 3.0]
    }
  • target_state (str): The desired target state for the qubits.

    self.target_state = "101"
  • train_json_file_path (str): File path to train.json.

    self.train_json_file_path = "var/train.json"
  • train_json_data (dict): Data loaded from train.json.

    self.train_json_data = {...}
  • logger (Logger): Logger instance for logging messages.

    self.logger = logging.getLogger()
  • data_json (dict): Data loaded from data.json.

    self.data_json = {...}

Methods

1. __init__(self, config_path='var')

Description: Initializes the Optimizer instance, setting up default values and paths.

Parameters:

  • config_path (str): Path to the configuration directory (default: 'var').

Data Structures:

  • Initializes empty or default values for attributes.

Method Calls:

  • None.

2. check_prerequisites(self)

Description: Checks if the specified optimizer exists and initializes qubits based on data.json.

Return:

  • None if successful.
  • dict with error code and message if an error occurs.

Data Structures:

  • Updates self.optimizer_class with the imported optimizer class.
  • Loads self.data_json from data.json.

Method Calls:

  • initialize_qubits(num_qubits)

Process:

  1. Optimizer Module Loading:

    • Constructs the module name based on self.optimizer.
    • Attempts to import the optimizer module and class.
    • If unsuccessful, returns an error:
      {"Error Code": 1111, "Message": "Optimizer not found."}
  2. Data Loading:

    • Attempts to load data.json.
    • If unsuccessful, returns an error:
      {"Error Code": 1112, "Message": "Data file not found."}
  3. Qubit Initialization:

    • Calls initialize_qubits(num_qubits) with the number of qubits from data.json.

3. initialize_qubits(self, num_qubits)

Description: Initializes qubit objects and stores them in self.Qubit_Object.

Parameters:

  • num_qubits (int): The number of qubits to initialize.

Data Structures:

  • Updates self.Qubit_Object with qubit instances.

Method Calls:

  • Instantiates Qubit objects.

Process:

  • Iterates over the range of num_qubits and creates a Qubit instance for each index.

4. extract_fields_from_job(self, job)

Description: Parses the current_job string to extract relevant fields.

Parameters:

  • job (str): The job string to parse.

Return:

  • fields (dict): A dictionary containing extracted fields if parsing is successful.
  • None if parsing fails.

Data Structures:

  • Uses regular expressions to extract fields.

Method Calls:

  • Uses re.match for pattern matching.

Process:

  • Defines a regex pattern to match the job string.
  • Extracts fields such as matrix_row_str, num_qubit, distributions, and state.

5. extract_matrix_from_string(self, matrix_str)

Description: Converts the matrix string from the job into a list of floating-point numbers.

Parameters:

  • matrix_str (str): The string containing matrix elements.

Return:

  • matrix_elements (list of float): List of matrix elements.
  • None if conversion fails.

Data Structures:

  • Parses the string and converts elements to floats.

Method Calls:

  • Uses re.findall to extract numbers.

Process:

  • Uses a regex pattern to find all numeric strings.
  • Converts each string to a float and appends to matrix_elements.

6. check_data_structure(self)

Description: Validates the structure of the current_job and extracts necessary fields.

Return:

  • None if successful.
  • dict with error code and message if validation fails.

Data Structures:

  • Updates self.dict_params_current_job with parsed data.

Method Calls:

  • extract_fields_from_job(self.current_job)
  • extract_matrix_from_string(matrix_row_str)

Process:

  1. Calls extract_fields_from_job to parse the job string.
  2. If parsing fails, logs an error and returns an error code.
  3. Extracts matrix elements using extract_matrix_from_string.
  4. If extraction fails, logs an error and returns an error code.
  5. Updates self.dict_params_current_job with matrix_elements.

7. evaluate(self, current_job)

Description: Evaluates the current job by parsing it and loading the state into the corresponding qubit.

Parameters:

  • current_job (str): The job string to evaluate.

Data Structures:

  • Updates self.current_job.
  • Updates qubit state in self.Qubit_Object.

Method Calls:

  • check_data_structure()
  • Qubit.load_state(state)

Process:

  1. Sets self.current_job to current_job.
  2. Calls check_data_structure() to validate and parse the job.
  3. If validation fails, the process stops.
  4. Loads the state into the corresponding qubit:
    num_qubit = int(self.dict_params_current_job["num_qubit"])
    state = self.dict_params_current_job["state"]
    self.Qubit_Object[num_qubit].load_state(state)

8. execute(self, current_job)

Description: Executes the optimization process for the current job and returns a new job string.

Parameters:

  • current_job (str): The job string to execute.

Return:

  • new_job (str): The updated job string after optimization.

Data Structures:

  • Interacts with optimizer instance.
  • Updates qubit loss function.

Method Calls:

  • evaluate(current_job)
  • Instantiates optimizer class.
  • optimizer.evaluate()
  • optimizer.calculate_loss()
  • optimizer.compute_gradient()
  • optimizer.optimize()
  • Qubit.load_function(loss)

Process:

  1. Calls evaluate(current_job) to parse and load the job.
  2. Instantiates the optimizer:
    optimizer = self.optimizer_class(self.data_json, self.dict_params_current_job["matrix_elements"], self.dict_params_current_job["state"])
  3. Runs optimization steps:
    • optimizer.evaluate()
    • loss = optimizer.calculate_loss()
    • optimizer.compute_gradient()
    • tuning_parameters, optimization_steps = optimizer.optimize()
  4. Updates qubit with loss function:
    num_qubit = int(self.dict_params_current_job["num_qubit"])
    self.Qubit_Object[num_qubit].load_function(loss)
  5. Constructs the new job string with updated tuning parameters and logs it.

9. start(self, optimizer, target_state)

Description: Initializes the optimizer process by validating the optimizer and target state.

Parameters:

  • optimizer (str): The name of the optimizer to use.
  • target_state (str): The desired target state for the qubits.

Return:

  • None if successful.
  • dict with error code and message if an error occurs.

Data Structures:

  • Updates self.optimizer and self.target_state.

Method Calls:

  • Validates the existence of the optimizer file.
  • check_prerequisites()

Process:

  1. Sets self.optimizer and self.target_state.
  2. Constructs the optimizer file path and checks if it exists.
  3. If not found, returns an error:
    {"Error Code": 1072, "Message": "Optimizer not found."}
  4. Calls check_prerequisites() to load the optimizer and initialize qubits.
  5. Validates the length of target_state against the number of qubits.
  6. Checks for the existence of train.json.
  7. Loads train.json data into self.train_json_data.
  8. Logs success messages.

10. optimize(self, measurement, training_matrix)

Description: Performs the optimization process using the given measurements and training matrix.

Parameters:

  • measurement (dict): Measurement data with keys as bitstrings and values as counts.
    measurement = {"000": 512, "101": 488}
  • training_matrix (list of list): The current training matrix.
    training_matrix = [
        [1.0, 2.0, 3.0, 4.0],
        [5.0, 6.0, 7.0, 8.0],
        [9.0, 10.0, 11.0, 12.0]
    ]

Return:

  • new_training_matrix (list of list): The updated training matrix after optimization.
  • None if an error occurs.

Data Structures:

  • Updates qubit training matrices and distributions.
  • Constructs new_training_matrix.

Method Calls:

  • encode_measurements(measurement)
  • Qubit.load_training_matrix(matrix_str)
  • Qubit.load_actual_distribution(distribution)
  • execute(current_job)

Process:

  1. Logs the start of the optimization process.
  2. Calls encode_measurements(measurement) to get qubit-wise measurements.
  3. Validates the length of training_matrix.
  4. For each qubit:
    • Converts training matrix row to string.
    • Loads the training matrix and actual distribution into the qubit.
  5. Initializes new_training_matrix as an empty list.
  6. For each qubit:
    • Constructs the current_job string.
    • Calls execute(current_job) to perform optimization.
    • Extracts updated matrix elements and appends to new_training_matrix.
  7. Logs success messages and returns new_training_matrix.

11. encode_measurements(self, measurement)

Description: Converts measurement data into qubit-wise distributions.

Parameters:

  • measurement (dict): Measurement data with keys as bitstrings and values as counts.

Return:

  • qubits_measurement (list of str): List of qubit measurement strings.
    qubits_measurement = ["(1:488; 0:512)", "(1:0; 0:1000)", "(1:488; 0:512)"]
  • None if an error occurs.

Data Structures:

  • Uses NumPy arrays to count measurements per qubit.

Method Calls:

  • None.

Process:

  1. Validates the length of bitstrings in measurement.
  2. Initializes a NumPy array qubits_measurement_count with zeros.
  3. Iterates over the measurements:
    • For each bitstring and count:
      • Updates qubits_measurement_count per qubit and bit value.
  4. Constructs qubits_measurement as a list of strings representing qubit distributions.

Method Call Relationships

  • start():

    • Calls check_prerequisites().
      • Calls initialize_qubits(num_qubits).
    • Validates optimizer file and train.json.
  • optimize():

    • Calls encode_measurements(measurement).
    • Iterates over qubits:
      • Calls Qubit.load_training_matrix(matrix_str).
      • Calls Qubit.load_actual_distribution(distribution).
      • Constructs current_job and calls execute(current_job).
        • Calls evaluate(current_job).
          • Calls check_data_structure().
            • Calls extract_fields_from_job(self.current_job).
            • Calls extract_matrix_from_string(matrix_row_str).
          • Calls Qubit.load_state(state).
        • Instantiates optimizer and runs optimization steps.
        • Calls Qubit.load_function(loss).

Data Structures Returned

  • Error Dictionaries:

    {"Error Code": 1111, "Message": "Optimizer not found."}
  • new_training_matrix (list of list):

    • After optimization, a list of lists containing updated tuning parameters.
    new_training_matrix = [
        [updated_values_for_qubit_0],
        [updated_values_for_qubit_1],
        ...
    ]
  • qubits_measurement (list of str):

    • Encoded measurement data per qubit.
    qubits_measurement = ["(1:488; 0:512)", "(1:0; 0:1000)", "(1:488; 0:512)"]
  • self.dict_params_current_job (dict):

    • Contains parsed fields from the job string, including matrix elements.
    self.dict_params_current_job = {
        "matrix_row_str": "1.0, 2.0, 3.0",
        "num_qubit": "0",
        "dist_0_0": "1",
        "vdist_0_1": "200",
        "dist_1_0": "0",
        "dist_1_1": "50",
        "state": "1",
        "matrix_elements": [1.0, 2.0, 3.0]
    }

Notes

  • Error Handling: The class methods return error dictionaries when issues are encountered, which include an error code and message.
  • Logging: The class uses a logger to log messages at various points, which helps in debugging and tracking the process flow.
  • Regular Expressions: Used extensively in parsing job strings and extracting data.

Example Usage

optimizer = Optimizer()
optimizer.start("AdaGradOptimizer", "101")
measurement = {"000": 512, "101": 488}
training_matrix = [
    [1.0, 2.0, 3.0, 4.0],
    [5.0, 6.0, 7.0, 8.0],
    [9.0, 10.0, 11.0, 12.0]
]
new_training_matrix = optimizer.optimize(measurement, training_matrix)

In this example:

  • The optimizer is initialized and started with the AdaGradOptimizer and a target state of "101".
  • Measurements are provided as a dictionary mapping bitstrings to counts.
  • A training matrix is provided.
  • The optimize() method returns an updated training matrix after performing optimization.

Clone this wiki locally