Skip to content

Commit

Permalink
Merge pull request #66 from isi-usc-edu/max-radin/two-step-pre
Browse files Browse the repository at this point in the history
Physical resource estimation script
  • Loading branch information
jp7745 authored Dec 16, 2024
2 parents 8fbc132 + 287f82a commit 1ae19cd
Show file tree
Hide file tree
Showing 6 changed files with 284 additions and 2 deletions.
10 changes: 9 additions & 1 deletion schemas/solution.schema.0.0.1.json
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,14 @@
"physical": {
"title": "Physical Quantum Resources",
"description": "Physical quantum resources used by the test performer. Exact structure is TBD.",
"type": "object"
"type": "object",
"properties": {
"num_physical_qubits": {
"title": "Number of Physical Qubits",
"description": "The number of physical qubits used by the solver.",
"type": "integer",
"minimum": 0
}
},
"logical": {
"title": "Logical Quantum Resources",
Expand Down Expand Up @@ -166,6 +173,7 @@
"maximum": 1
}
}

}
}
},
Expand Down
16 changes: 16 additions & 0 deletions scripts/PRE_config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"solver_uuid": "5dad4064-cd11-412f-85cb-d722afe3b3de",
"quantum_hardware_parameters": {
"num_factories": 4,
"physical_error_rate": 1e-4,
"cycle_time_microseconds": 1
},
"quantum_hardware_description": "Optimistic superconducting hardware model based on that described in https://arxiv.org/abs/2011.03494.",
"contact_info": [
{
"name": "Max Radin",
"email": "[email protected]",
"institution": "L3Harris"
}
]
}
19 changes: 19 additions & 0 deletions scripts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,25 @@ The script will generate logical quantum resource estimates for a subset of Hami
This script is currently based on the qubitized quantum phase estimation with double factorization algorithm.


## Generating physical quantum resource estimates as `solution.json` files.

`./compute_all_PREs_script.py`

```bash
options:
-h, --help show this help message and exit
-i INPUT_DIR, --input_dir INPUT_DIR
Specify directory for solution logical resource estiamtes (.json files)
-o OUTPUT_DIR, --output_dir OUTPUT_DIR
Specify directory to save physical resource estimates to (.json files)
--PRE_config_file PRE_CONFIG_FILE
A JSON file with configuration options and hyperparameters for PRE and a `solver` UUID.
```

This script ingests solution files containing logical resource estimates and generates new solution files that contain physical resource estimates.
Note that these solution files will have new solution IDs that differ from the input LRE solution files.
It is also recommended to specify a solver UUID in the PRE config file that is different from the solver UUID for the LRE.
See [`PRE_config.json`](PRE_config.json) for an example PRE config file.

## Generating `performance_metrics.json` files

Expand Down
2 changes: 1 addition & 1 deletion scripts/compute_all_LREs_script.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ def get_lqre(
circuit_generation_end_time
- circuit_generation_start_time
).total_seconds(),
}
},
},
}
)
Expand Down
238 changes: 238 additions & 0 deletions scripts/compute_all_PREs_script.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
#!/usr/bin/env python3

# Copyright 2024 L3Harris Technologies, Inc.

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at

# http://www.apache.org/licenses/LICENSE-2.0

# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


import argparse
import copy
import datetime
import json
import logging
import os
from importlib.metadata import version
from typing import Any
from uuid import uuid4

from qualtran.surface_code.algorithm_summary import AlgorithmSummary
from qualtran.surface_code.ccz2t_cost_model import (
CCZ2TFactory,
get_ccz2t_costs_from_grid_search,
iter_ccz2t_factories,
)
from qualtran.surface_code.data_block import SimpleDataBlock
from qualtran.surface_code.physical_cost import PhysicalCost


class NoFactoriesFoundError(Exception):
pass


logger = logging.getLogger()
logger.setLevel(logging.INFO)
console_handler = logging.StreamHandler()
file_handler = logging.FileHandler("compute_all_QREs_scripts.log.txt", delay=False)
formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
handlers = [console_handler, file_handler]
for h in handlers:
h.setFormatter(formatter)
logger.addHandler(h)


def get_physical_cost(
num_logical_qubits: int,
num_T_gates: int,
hardware_failure_tolerance_per_shot: float,
n_factories: int,
physical_error_rate: float,
cycle_time_us: float,
) -> tuple[PhysicalCost, CCZ2TFactory, SimpleDataBlock]:
n_magic = AlgorithmSummary(t_gates=num_T_gates)
try:
best_cost, best_factory, best_data_block = get_ccz2t_costs_from_grid_search(
n_magic=n_magic,
n_algo_qubits=num_logical_qubits,
error_budget=hardware_failure_tolerance_per_shot,
cycle_time_us=cycle_time_us,
phys_err=physical_error_rate,
factory_iter=iter_ccz2t_factories(n_factories=n_factories),
cost_function=(lambda pc: pc.duration_hr),
)
return best_cost, best_factory, best_data_block
except TypeError:
raise NoFactoriesFoundError(
f"No factories found that meet the performance requirements."
)


def get_pqre(solution_lre: dict, config: dict) -> dict[str, Any]:
logging.info(f"solution UUID: {solution_lre['solution_uuid']}")

solution_pre = copy.deepcopy(solution_lre)
for task_solution_data in solution_pre["solution_data"]:
logging.info(f"Analyzing task {task_solution_data['task_uuid']}...")
try:
physical_resource_estimation_start = datetime.datetime.now()

cost, factory, data_block = get_physical_cost(
num_logical_qubits=task_solution_data["quantum_resources"]["logical"][
"num_logical_qubits"
],
num_T_gates=task_solution_data["quantum_resources"]["logical"][
"num_T_gates_per_shot"
],
hardware_failure_tolerance_per_shot=task_solution_data[
"quantum_resources"
]["logical"]["hardware_failure_tolerance_per_shot"],
n_factories=config["quantum_hardware_parameters"]["num_factories"],
physical_error_rate=config["quantum_hardware_parameters"][
"physical_error_rate"
],
cycle_time_us=config["quantum_hardware_parameters"][
"cycle_time_microseconds"
],
)
physical_resource_estimation_end = datetime.datetime.now()
logging.info(
f"Physical resource estimation time: {(physical_resource_estimation_end - physical_resource_estimation_start).total_seconds()} seconds."
)

algorithm_runtime_seconds = cost.duration_hr * 60 * 60
num_physical_qubits = cost.footprint
task_solution_data["run_time"]["algorithm_run_time"] = {
"seconds": algorithm_runtime_seconds,
}
task_solution_data["run_time"]["overall_time"] = {
"seconds": task_solution_data["run_time"]["preprocessing_time"][
"seconds"
]
+ algorithm_runtime_seconds
}

task_solution_data["quantum_resources"]["physical"] = {
"num_physical_qubits": num_physical_qubits,
"distillation_layer_1_code_distance": factory.base_factory.distillation_l1_d,
"distillation_layer_2_code_distance": factory.base_factory.distillation_l2_d,
"data_code_distance": data_block.data_d,
"data_routing_overhead": data_block.routing_overhead,
}
except NoFactoriesFoundError:
logging.info(
f"No factories found that meet the performance requirements. Skipping physical cost estimation."
)

solution_uuid = str(uuid4())
current_time = datetime.datetime.now(datetime.timezone.utc)
current_time_string = current_time.strftime("%Y-%m-%dT%H:%M:%S.%f") + "Z"

solution_pre["solution_uuid"] = solution_uuid
solution_pre["creation_timestamp"] = current_time_string
solution_pre["contact_info"] = config["contact_info"]
solution_pre["solver_details"]["software_details"].append(
{
"software_name": "qualtran",
"software_version": version("qualtran"),
}
)

solution_pre["solver_details"]["quantum_hardware_details"] = {
"quantum_hardware_description": config["quantum_hardware_description"],
"quantum_hardware_parameters": config["quantum_hardware_parameters"],
}
solution_pre["solver_details"]["solver_uuid"] = config["solver_uuid"]
solution_pre["digital_signature"] = None
solution_pre["solver_details"][
"logical_resource_estimate_solution_uuid"
] = solution_lre["solution_uuid"]
solution_pre["solver_details"][
"logical_resource_estimate_solver_uuid"
] = solution_lre["solver_details"]["solver_uuid"]

return solution_pre


def main(args: argparse.Namespace) -> None:

config = json.load(open(args.PRE_config_file, "r"))

overall_start_time = datetime.datetime.now()
logging.info(f"===============================================")
logging.info(f"overall start time: {overall_start_time}")
logging.info(f"input directory: {args.input_dir}")

input_dir = args.input_dir

solution_lre_files = os.listdir(input_dir)
logging.info(f"parsing {len(solution_lre_files)} files in the input directory")
for s in solution_lre_files:
logging.info(f"file: {s}")

for solution_lre_file_name in solution_lre_files:
solution_lre_path = os.path.join(input_dir, solution_lre_file_name)
logging.info(f"parsing {solution_lre_path}")
with open(solution_lre_path, "r") as jf:
solution_lre = json.load(jf)

resource_estimate = get_pqre(solution_lre=solution_lre, config=config)
if len(resource_estimate["solution_data"]) > 0:
with open(
os.path.join(
args.output_dir,
f"{resource_estimate['problem_instance_uuid']}_sol_{resource_estimate['solution_uuid']}.json",
),
"w",
) as f:
json.dump(resource_estimate, f, indent=4)

# Print overall time.
# ===============================================================
overall_stop_time = datetime.datetime.now()
logging.info(f"done.")
logging.info(f"overall start time: {overall_start_time}")
logging.info(f"overall stop time: {overall_stop_time}")
logging.info(
f"run time (seconds): {(overall_stop_time - overall_start_time).total_seconds()}"
)


if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="a script to calculate Physical Resource Estimates (PREs) for all solution files containing Logical Resource Estimates (LREs). Outputs are solution.uuid.json files."
)

parser.add_argument(
"-i",
"--input_dir",
type=str,
required=True,
help="Specify directory for solution logical resource estiamtes (.json files)",
)

parser.add_argument(
"-o",
"--output_dir",
type=str,
required=True,
help="Specify directory to save physical resource estimates to (.json files)",
)

parser.add_argument(
"--PRE_config_file",
type=str,
required=True,
help="A JSON file with configuration options and hyperparameters for PRE and a `solver` UUID.",
)

args = parser.parse_args()
main(args)
1 change: 1 addition & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ python_requires = >=3.8

install_requires =
pyLIQTR>=1.2.0
qualtran==0.2.0
pyscf
openfermion
openfermionpyscf
Expand Down

0 comments on commit 1ae19cd

Please sign in to comment.