Skip to content

Commit

Permalink
Quantum Chemistry Notebook Documentation (#30)
Browse files Browse the repository at this point in the history
* Updated documentation content for quantum chemistry notebook about initial state prep and QPE for GSEE

* Added additional information regarding HF, measuring activation energy, and cleaning up the code

* changing name of xyz file from Co4O4 to Co2O9H12

* improved documentation for quantum chemistry notebook

* Updated pylint badge

* added a references section in the quantum chemistry notebook

* pylint fixes for algo_utils

* some pylint fixes for hamiltoniain_utils

* import grouping pylint fixes

* some pylint changes for utils.py

* Updated pylint badge

* Added a sentence to clarify the notebooks computational targets

* reran ap notebook for grabbing re

* made changes to specify how the scaling factor is defined

* reran notebook and assured resource estimates follow standard and makes sense

* Updated pylint badge

* fixed typo for nb

* adding clarified paragraph given feedback

* fixed typos

* rewrote active space comment

---------

Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
  • Loading branch information
JonhasSC and github-actions[bot] authored Apr 29, 2024
1 parent 44580b4 commit f05c9cb
Show file tree
Hide file tree
Showing 7 changed files with 703 additions and 159 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
![pylint](https://img.shields.io/badge/PyLint-9.08-yellow?logo=python&logoColor=white)
![pylint](https://img.shields.io/badge/PyLint-9.60-yellow?logo=python&logoColor=white)
# Quantum Computing Application Specifications

Documentation of Applications for Quantum Computers
Expand Down
File renamed without changes.
File renamed without changes.
770 changes: 641 additions & 129 deletions notebooks/PhotosynthesisExample.ipynb

Large diffs are not rendered by default.

20 changes: 13 additions & 7 deletions src/qca/utils/algo_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,24 @@
import random
import numpy as np
import networkx as nx

from cirq import Circuit
from openfermion import count_qubits
from cirq.contrib import qasm_import
from pyLIQTR.utils.Hamiltonian import Hamiltonian

from openfermion import count_qubits
from openfermion.ops.operators import QubitOperator
from openfermion.circuits import trotter_steps_required, error_bound
from openfermion.circuits.trotter_exp_to_qgates import trotterize_exp_qubop_to_qasm

from pyLIQTR.utils.Hamiltonian import Hamiltonian
from pyLIQTR.utils.utils import open_fermion_to_qasm
from pyLIQTR.circuits.qsp import generate_QSP_circuit
from pyLIQTR.utils.qsp_helpers import print_to_openqasm
from openfermion.circuits import trotter_steps_required, error_bound
from qca.utils.utils import circuit_estimate, estimate_cpt_resources
from pyLIQTR.gate_decomp.cirq_transforms import clifford_plus_t_direct_transform
from openfermion.circuits.trotter_exp_to_qgates import trotterize_exp_qubop_to_qasm
from pyLIQTR.phase_factors.fourier_response.fourier_response import Angler_fourier_response

from qca.utils.utils import circuit_estimate, estimate_cpt_resources

def estimate_qsp(
pyliqtr_hamiltonian: Hamiltonian,
evolution_time:float,
Expand Down Expand Up @@ -58,7 +62,9 @@ def find_hamiltonian_ordering(of_hamiltonian: QubitOperator) -> list:
#ordering hamiltonian terms by performing edge coloring to make optimal trotter ordering
#assuming that any 2 body interactions are ZZ
sorted_terms = sorted(list(of_hamiltonian.terms.keys()))
sorted_terms.sort(key=lambda x: len(x) * 100 + ord(x[0][1])) #Z and X get translated to 90 and 88 respectively, multiplying by 100 ensures interacting term weight is considered

#Z and X get translated to 90 and 88 respectively, multiplying by 100 ensures interacting term weight is considered
sorted_terms.sort(key=lambda x: len(x) * 100 + ord(x[0][1]))
one_body_terms_ordered = list(filter(lambda x: len(x) == 1, sorted_terms))
two_body_terms = list(filter(lambda x: len(x) == 2, sorted_terms))

Expand All @@ -77,7 +83,7 @@ def find_hamiltonian_ordering(of_hamiltonian: QubitOperator) -> list:
two_body_terms[i] = term

two_body_terms.sort(key=lambda x: x[2])
two_body_terms_ordered = list()
two_body_terms_ordered = []
for (i,term) in enumerate(two_body_terms):
new_item = (term[0],term[1])
two_body_terms_ordered.append(new_item)
Expand Down
21 changes: 13 additions & 8 deletions src/qca/utils/hamiltonian_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ def nx_longitudinal_ising_terms(graph,p,magnitude=1) -> list[tuple[str, float]]:
n = len(graph.nodes)
for n1, n2 in graph.edges:
weight = magnitude if random.random() < p else -magnitude
curr_hamil_string = n * 'I'
curr_hamil_string = n * 'I'
for idx in range(len(graph)):
if idx == n1 or idx == n2:
curr_hamil_string = f'{curr_hamil_string[:idx]}Z{curr_hamil_string[idx+1:]}'
Expand All @@ -146,7 +146,7 @@ def nx_transverse_ising_terms(graph: Graph,p,magnitude=0.1) -> list[tuple[str, f
n = len(graph)
for idx in range(n):
w = magnitude if random.random() < p else -magnitude
curr_hamil_string = n * 'I'
curr_hamil_string = n * 'I'
for k in range(n):
if idx == k:
curr_hamil_string = f'{curr_hamil_string[:idx]}X{curr_hamil_string[idx+1:]}'
Expand All @@ -161,14 +161,19 @@ def nx_triangle_lattice(lattice_size: int) -> Graph:
graph.add_edge((i,j),(i+1,j+1))
return graph

def generate_triangle_hamiltonian(lattice_size: int, longitudinal_weight_prob:float=0.5, transverse_weight_prob:float=1):
def generate_triangle_hamiltonian(lattice_size: int,
longitudinal_weight_prob:float=0.5,
transverse_weight_prob:float=1):
graph = nx_triangle_lattice(lattice_size)
graph = flatten_nx_graph(graph)
H_transverse = nx_transverse_ising_terms(graph, transverse_weight_prob)
H_longitudinal = nx_longitudinal_ising_terms(graph, longitudinal_weight_prob)
return H_transverse, H_longitudinal

def generate_square_hamiltonian(lattice_size: int, dim:int, longitudinal_weight_prob:float=0.5, transverse_weight_prob:float=1):
def generate_square_hamiltonian(lattice_size: int,
dim:int,
longitudinal_weight_prob:float=0.5,
transverse_weight_prob:float=1):
dimensions = (lattice_size, lattice_size) if dim == 2 else (lattice_size, lattice_size, lattice_size)
graph = grid_graph(dim=dimensions)
graph = flatten_nx_graph(graph)
Expand Down Expand Up @@ -196,20 +201,20 @@ def assign_hexagon_labels(graph:Graph, x:str='X', y:str='Y', z:str='Z'):
if r2 - r1 < 0 or c2 - c1 < 0:
r1, r2 = r2, r1
c1, c2 = c2, c1

# now that they are ordered correctly, we can assign labels
label = ''
if c1 == c2:
label = z
# You can differentiate X and Y labels based off nx's node label parity
elif (((r1 % 2) + (c1 % 2)) % 2 == 0):
elif ((r1 % 2) + (c1 % 2)) % 2 == 0:
label = y
else:
label = x

graph[n1][n2]['label'] = label

def assign_directional_triangular_labels(g:Graph, lattice_size:int) -> None:
def assign_directional_triangular_labels(g:Graph, lattice_size:int) -> None:
for i in range(lattice_size - 1):
for j in range(lattice_size - 1):
g[(i,j)][(i+1,j)]['label'] = 'Z'
Expand Down
49 changes: 35 additions & 14 deletions src/qca/utils/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@
import re
import json
import time
import pandas as pd
from statistics import median

import pandas as pd

import matplotlib.pyplot as plt
from pyLIQTR.utils.utils import count_T_gates

from cirq import Circuit, AbstractCircuit

from pyLIQTR.utils.utils import count_T_gates
from pyLIQTR.utils.qsp_helpers import circuit_decompose_once, print_to_openqasm
from pyLIQTR.gate_decomp.cirq_transforms import clifford_plus_t_direct_transform

Expand All @@ -30,7 +34,7 @@ def get_T_depth_wire(cpt_circuit: AbstractCircuit):
opstr = str(operator)
if opstr[0] == 'T':
reg_label = opstr[opstr.find("(")+1:opstr.find(")")]
if not reg_label in count_dict:
if reg_label not in count_dict:
count_dict[reg_label] = 1
else:
count_dict[reg_label] += 1
Expand Down Expand Up @@ -87,7 +91,8 @@ def get_T_depth(cpt_circuit: AbstractCircuit):
def gen_resource_estimate(cpt_circuit: AbstractCircuit,
is_extrapolated: bool,
total_steps:int = -1,
circ_occurences:int = -1) -> dict:
circ_occurences:int = -1,
bits_precision:int=1) -> dict:
'''
Given some clifford + T circuit and a given filename, we grab the logical resource estimates
from the circuit and then write it to disk. The function also returns the resource dictionary
Expand Down Expand Up @@ -115,12 +120,19 @@ def gen_resource_estimate(cpt_circuit: AbstractCircuit,
}

if total_steps > 0 and is_extrapolated:
resource_estimate['t_depth'] = resource_estimate['t_depth'] * total_steps
resource_estimate['t_count'] = resource_estimate['t_count'] * total_steps
resource_estimate['max_t_depth_wire'] = resource_estimate['max_t_depth_wire'] * total_steps
resource_estimate['gate_count'] = resource_estimate['gate_count'] * total_steps
resource_estimate['circuit_depth'] = resource_estimate['circuit_depth'] * total_steps
resource_estimate['clifford_count'] = resource_estimate['clifford_count'] * total_steps
scaling_factor = total_steps
elif bits_precision > 0:
scaling_factor = pow(2, bits_precision - 1)
else:
scaling_factor = None

if scaling_factor:
resource_estimate['t_depth'] = resource_estimate['t_depth'] * scaling_factor
resource_estimate['t_count'] = resource_estimate['t_count'] * scaling_factor
resource_estimate['max_t_depth_wire'] = resource_estimate['max_t_depth_wire'] * scaling_factor
resource_estimate['gate_count'] = resource_estimate['gate_count'] * scaling_factor
resource_estimate['circuit_depth'] = resource_estimate['circuit_depth'] * scaling_factor
resource_estimate['clifford_count'] = resource_estimate['clifford_count'] * scaling_factor

if circ_occurences > 0:
resource_estimate['subcicruit_occurrences'] = circ_occurences
Expand Down Expand Up @@ -168,12 +180,13 @@ def circuit_estimate(
numsteps: int,
circuit_name: str,
algo_name:str,
bits_precision:int=1,
write_circuits:bool = False
) -> AbstractCircuit:
if not os.path.exists(outdir):
os.makedirs(outdir)
subcircuit_counts = dict()

subcircuit_counts = {}
for moment in circuit:
for operation in moment:
gate_type = type(operation.gate)
Expand Down Expand Up @@ -213,7 +226,8 @@ def circuit_estimate(
subcircuit_name = subcircuit_counts[gate][2]
resource_estimate = gen_resource_estimate(subcircuit,
is_extrapolated=False,
circ_occurences=subcircuit_counts[gate][0])
circ_occurences=subcircuit_counts[gate][0],
bits_precision=bits_precision)
subcircuit_info = {subcircuit_name:resource_estimate}
subcircuit_re.append(subcircuit_info)

Expand All @@ -230,6 +244,13 @@ def circuit_estimate(
total_T_depth_wire += subcircuit_counts[gate][0] * t_depth_wire * numsteps
total_T_count += subcircuit_counts[gate][0] * t_count * numsteps
total_clifford_count += subcircuit_counts[gate][0] * clifford_count * numsteps

# total_gate_count += subcircuit_counts[gate][0] * gate_count * numsteps * pow(2, bits_precision - 1)
# total_gate_depth += subcircuit_counts[gate][0] * gate_depth * numsteps * pow(2, bits_precision - 1)
# total_T_depth += subcircuit_counts[gate][0] * t_depth * numsteps * pow(2, bits_precision - 1)
# total_T_depth_wire += subcircuit_counts[gate][0] * t_depth_wire * numsteps * pow(2, bits_precision - 1)
# total_T_count += subcircuit_counts[gate][0] * t_count * numsteps * pow(2, bits_precision - 1)
# total_clifford_count += subcircuit_counts[gate][0] * clifford_count * numsteps * pow(2, bits_precision - 1)

outfile_data = f'{outdir}{circuit_name}_high_level.json'
total_resources = {
Expand Down Expand Up @@ -268,4 +289,4 @@ def re_as_json(main_estimate:dict, estimates:list[dict], file_name:str, algo_nam
with open(file_name, 'w') as f:
json.dump(main_estimate, f,
indent=4,
separators=(',', ': '))
separators=(',', ': '))

0 comments on commit f05c9cb

Please sign in to comment.