Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ZZMax randomisation #99

Draft
wants to merge 8 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/build_and_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
- name: Build qermit
if: github.event_name == 'pull_request'
run: |
pip install -e .[tests] -v
pip install .[tests] -v
- name: Test qermit
if: github.event_name == 'pull_request'
run: |
Expand All @@ -39,7 +39,7 @@ jobs:
- name: Checkout
uses: actions/checkout@v4
- name: Install Qermit
run: pip install -e .[docs] -v
run: pip install .[docs] -v
- name: Build Docs
run: |
cd docs_src
Expand Down
300 changes: 289 additions & 11 deletions poetry.lock

Large diffs are not rendered by default.

10 changes: 6 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@ readme = "README.md"

[tool.poetry.dependencies]
python = ">=3.10, <3.13"
pytket-qiskit = "^0.50.0"
pytket-qiskit = "0.50"
matplotlib = "^3.8.3"
importlib-resources = "^6.4.0"
pytket-quantinuum = "^0.32"

pytest = {version="^8.1.1", optional=true}
mypy = {version="^1.9.0", optional=true}
Expand All @@ -25,8 +27,8 @@ sphinx = {version="^7.2.6", optional=true}
pytest-cov = {version="^4.1.0", optional=true}
sphinx-book-theme = {version="^1.1.2", optional=true}
qiskit-ibm-provider = {version="^0.10.0", optional=true}
pytket-quantinuum = "0.32"
pytket-pecos = {version="^0.1.23", optional=true}

[tool.poetry.extras]
tests = ["pytest", "mypy", "flake8", "pytest-cov", "qiskit-ibm-provider", "pytket-quantinuum"]
docs = ["sphinx", "sphinx-book-theme", "pytket-quantinuum"]
tests = ["pytest", "mypy", "flake8", "pytest-cov", "qiskit-ibm-provider", "pytket-pecos"]
docs = ["sphinx", "sphinx-book-theme"]
7 changes: 7 additions & 0 deletions qermit/frame_randomisation/frame_randomisation.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,17 @@
from enum import Enum
from pytket import OpType
from pytket.passes import auto_rebase_pass
from .h_series_randomisation import gen_h_series_randomised_circuit


class FrameRandomisation(Enum):

@staticmethod
def HSeriesRandomisation(
circuit: Circuit, shots: int,
) -> List[CircuitShots]:
return [CircuitShots(Circuit=gen_h_series_randomised_circuit(circuit), Shots=shots)]

@staticmethod
def PauliFrameRandomisation(
circuit: Circuit, shots: int, samples: int
Expand Down
75 changes: 75 additions & 0 deletions qermit/frame_randomisation/h_series_randomisation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
from pytket import Circuit, OpType, wasm
import itertools
from pytket.unit_id import BitRegister, QubitRegister
from pathlib import Path
import importlib_resources


def get_wfh():

package_path = importlib_resources.files("qermit")
wasm_file = f"{package_path}/frame_randomisation/qermit_rus.wasm"
return wasm.WasmFileHandler(wasm_file)


def gen_h_series_randomised_circuit(circuit):

wfh = get_wfh()

randomised_circuit = Circuit()
randomisation_reg_dict = {}
for q_register in circuit.q_registers:
randomised_circuit.add_q_register(q_register)
for qubit in q_register:
randomisation_reg_dict[qubit] = randomised_circuit.add_c_register(f"randomisation_{qubit.to_list()}", 4)
for c_register in circuit.c_registers:
randomised_circuit.add_c_register(c_register)

seed_size = 16
seed_c_reg = BitRegister(name='seed_c', size=seed_size)
randomised_circuit.add_c_register(seed_c_reg)

if circuit.n_qubits < seed_size:
seed_q_reg = QubitRegister(
name='seed_q',
size=seed_size-sum(q_register.size for q_register in randomised_circuit.q_registers)
)
randomised_circuit.add_q_register(seed_q_reg)

all_qubits = list(itertools.chain.from_iterable([q_register.to_list() for q_register in randomised_circuit.q_registers]))
for qubit, cbit in zip(all_qubits[:seed_size], seed_c_reg.to_list()):
randomised_circuit.H(qubit)
randomised_circuit.Measure(qubit, cbit)
randomised_circuit.Reset(qubit)
randomised_circuit.add_wasm_to_reg("seed_randomisation", wfh, [seed_c_reg], [])

for command in circuit:

randomisation_reg = randomisation_reg_dict[command.args[0]]

if command.op.type == OpType.ZZMax:
randomised_circuit.add_wasm_to_reg("write_randomisation", wfh, [], [randomisation_reg])
randomised_circuit.Z(command.qubits[0], condition=randomisation_reg[0])
randomised_circuit.Z(command.qubits[1], condition=randomisation_reg[1])
randomised_circuit.X(command.qubits[0], condition=randomisation_reg[2])
randomised_circuit.X(command.qubits[1], condition=randomisation_reg[3])

randomised_circuit.add_gate(command.op, command.args)

if command.op.type == OpType.ZZMax:
randomised_circuit.Z(
command.qubits[0],
condition=randomisation_reg[0] ^ randomisation_reg[2] ^ randomisation_reg[3]
)
randomised_circuit.Z(
command.qubits[1],
condition=randomisation_reg[1] ^ randomisation_reg[2] ^ randomisation_reg[3]
)
randomised_circuit.X(command.qubits[0], condition=randomisation_reg[2])
randomised_circuit.X(command.qubits[1], condition=randomisation_reg[3])

randomised_circuit.Phase(0.5, condition=randomisation_reg[2])
randomised_circuit.Phase(0.5, condition=randomisation_reg[3])
randomised_circuit.Phase(-1, condition=randomisation_reg[2] & randomisation_reg[3])

return randomised_circuit
Binary file added qermit/frame_randomisation/qermit_rus.wasm
Binary file not shown.
6 changes: 3 additions & 3 deletions qermit/noise_model/noise_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ def to_ptm(self) -> Tuple[NDArray, Dict[Tuple[Pauli, ...], int]]:
return ptm, pauli_index

@classmethod
def from_ptm(cls, ptm: NDArray, pauli_index: Dict[Tuple[Pauli, ...], int]) -> ErrorDistribution:
def from_ptm(cls, ptm: NDArray, pauli_index: Dict[Tuple[Pauli, ...], int], rng: Generator = np.random.default_rng()) -> ErrorDistribution:
"""Convert a Pauli Transfer Matrix (PTM) to an error distribution.

:param ptm: Pauli Transfer Matrix to convert. Should be a 4^n by 4^n matrix
Expand Down Expand Up @@ -201,7 +201,7 @@ def from_ptm(cls, ptm: NDArray, pauli_index: Dict[Tuple[Pauli, ...], int]) -> Er
for error, index in pauli_index.items()
if (error_rate_list[index] > 10**(-6)) and error != tuple(Pauli.I for _ in range(int(n_qubit)))
}
return cls(distribution=distribution)
return cls(distribution=distribution, rng=rng)

@property
def n_qubits(self) -> int:
Expand Down Expand Up @@ -376,7 +376,7 @@ def scale(self, scaling_factor: float) -> ErrorDistribution:

ptm, pauli_index = self.to_ptm()
scaled_ptm = fractional_matrix_power(ptm, scaling_factor)
return ErrorDistribution.from_ptm(ptm=scaled_ptm, pauli_index=pauli_index)
return ErrorDistribution.from_ptm(ptm=scaled_ptm, pauli_index=pauli_index, rng=self.rng)


class LogicalErrorDistribution:
Expand Down
2 changes: 1 addition & 1 deletion qermit/noise_model/transpiler_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ def process_circuit(
n_shots: int,
**kwargs,
) -> ResultHandle:
"""[summary]
"""Run a single circuit.

:param circuit: Submits circuit to run on noisy backend.
:type circuit: Circuit
Expand Down
16 changes: 16 additions & 0 deletions qermit_rus/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions qermit_rus/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "qermit_rus"
version = "0.1.0"
edition = "2021"

[lib]
# cdylib specifies a dynamic system library will be produced. This is used when
# compiling a dynamic library to be loaded from another language, as is the
# case for hybrid compute.
crate-type = ["cdylib"]

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
fastrand = "2.0.1"
68 changes: 68 additions & 0 deletions qermit_rus/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// For hybrid compute to work, [no_mangle] must be at the top of all functions
// we plan to call from our quantum program
// For more info see: [Calling Rust from Other Languages]
// (https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html?highlight=no_mangle#calling-rust-functions-from-other-languages)
#[no_mangle]
fn init(){
// This function can have nothing it in, or load some initial function.
// It is needed when passed via the Quantinuum API to warm up the wasm execution environment.
// It can also be used to set up a global state.
}

// fn get_bit(bit_string: u8, index: u32) -> u8 {
// let base: u8 = 2;
// (bit_string & base.pow(index)) >> index
// }

// fn get_correction(initial_pauli: u8) -> u8 {

// // calculate x_1
// let mut correct_pauli: u8 = get_bit(initial_pauli, 3);
// correct_pauli = correct_pauli << 1;

// // calculate x_0
// correct_pauli = correct_pauli ^ get_bit(initial_pauli, 2);
// correct_pauli = correct_pauli << 1;

// // calculate z_1
// correct_pauli = correct_pauli ^ get_bit(initial_pauli, 1);
// correct_pauli = correct_pauli ^ get_bit(initial_pauli, 2);
// correct_pauli = correct_pauli ^ get_bit(initial_pauli, 3);
// correct_pauli = correct_pauli << 1;

// // calculate z_0
// correct_pauli = correct_pauli ^ get_bit(initial_pauli, 0);
// correct_pauli = correct_pauli ^ get_bit(initial_pauli, 2);
// correct_pauli = correct_pauli ^ get_bit(initial_pauli, 3);

// correct_pauli
// }

#[no_mangle]
pub extern "C" fn write_randomisation() -> u8 {

// let initial_pauli: u8 = fastrand::u8(..);
// let correct_pauli: u8 = get_correction(initial_pauli);

// initial_pauli | correct_pauli << 4
fastrand::u8(..)
}

#[no_mangle]
pub extern "C" fn seed_randomisation(seed: u16) {
fastrand::seed(seed as u64);
}

// #[cfg(test)]
// mod tests {
// use super::*;

// #[test]
// fn correct_correction() {
// assert_eq!(get_correction(14), 14);
// assert_eq!(get_correction(9), 10);
// assert_eq!(get_correction(4), 7);
// assert_eq!(get_correction(2), 2);
// assert_eq!(get_correction(6), 5);
// }
// }
Loading
Loading