-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Jobs directory and depolarizing channel (#166)
Adds a jobs directory for a Job object combining a Circuit and a ParameterSweep. Provides an initial implementation of a depolarizing channel simulator using this framework.
- Loading branch information
1 parent
93db9eb
commit 35af7f2
Showing
7 changed files
with
421 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# Copyright 2018 Google LLC | ||
# | ||
# 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 | ||
# | ||
# https://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. | ||
|
||
"""Package for contributions. | ||
Any contributions not ready for full production can be put in a subdirectory in | ||
this package. | ||
""" | ||
|
||
from cirq.contrib import jobs |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
# Copyright 2018 Google LLC | ||
# | ||
# 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 | ||
# | ||
# https://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. | ||
|
||
"""Package for handling a full quantum job. | ||
Types and methods related to transforming circuits in preparation of sending | ||
them to Quantum Engine. Contains classes to help with adding parameter | ||
sweeps and error simulation. | ||
""" | ||
|
||
from cirq.contrib.jobs.job import ( | ||
Job, | ||
) | ||
from cirq.contrib.jobs.depolarizer_channel import ( | ||
DepolarizerChannel, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
# Copyright 2017 Google LLC | ||
# | ||
# 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 | ||
# | ||
# https://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. | ||
|
||
"""Error simulator that adds randomly activated error gates after every moment. | ||
""" | ||
import random | ||
from cirq.api.google.v1.params_pb2 import ParameterSweep | ||
from cirq.api.google.v1.params_pb2 import SingleParameterSweep | ||
from cirq.circuits.circuit import Circuit | ||
from cirq.circuits.circuit import Moment | ||
from cirq.contrib.jobs import Job | ||
from cirq.google import xmon_gates | ||
from cirq.study.parameterized_value import ParameterizedValue | ||
|
||
|
||
class DepolarizerChannel(object): | ||
"""Depolarizing Channel Transformer for error simulation. | ||
This class simulates errors in a quantum circuit by randomly | ||
introducing error gates in between each moment. This simulates | ||
noise by decohering random qubits at each step. This transform | ||
is intended only for classical simulations only. | ||
This class currently only supports adding a Pauli-Z gate at | ||
each step. | ||
Attributes: | ||
probability: Probability of a qubit being affected in a given moment | ||
repetitions: Number of simulations to create. | ||
""" | ||
|
||
# Prefix for ParameterizedValues related to error simulation | ||
_parameter_name = 'error_parameter' | ||
|
||
def __init__(self, probability=0.001, repetitions=1): | ||
self.p = probability | ||
self.repetitions = repetitions | ||
|
||
def transform_job(self, job): | ||
"""Creates a new job object with depolarizing channel. | ||
This job will contain the existing Job's circuit with an error gate per | ||
qubit at every moment. Creates the parameter sweep for each gate and | ||
populates with random values as per the specifications of the | ||
depolarizer channel. | ||
Does not yet support augmenting jobs that already have parameter sweeps. | ||
Args: | ||
job: Job object to transform | ||
Returns: | ||
A new Job object that contains a circuit with up to double the | ||
moments as the original job, with every other moment being a | ||
moment containing error gates. It will also contain a | ||
ParameterSweep containing values for each error gate. Note that | ||
moments that contain no error gates for any repetition will be | ||
automatically omitted. | ||
""" | ||
# A set for quick lookup of pre-existing qubits | ||
qubit_set = set() | ||
# A list with deterministic qubit order | ||
qubit_list = [] | ||
circuit = job.circuit | ||
|
||
# Retrieve the set of qubits used in the circuit | ||
for moment in circuit.moments: | ||
for op in moment.operations: | ||
for qubit in op.qubits: | ||
if qubit not in qubit_set: | ||
qubit_set.add(qubit) | ||
qubit_list.append(qubit) | ||
|
||
# Add error circuits | ||
moments = [] | ||
parameter_number = 0 | ||
|
||
sweep = ParameterSweep() | ||
sweep.repetitions = 1 | ||
sweep.sweep.factors.add() | ||
add_gate = False | ||
for moment in circuit.moments: | ||
moments.append(moment) | ||
|
||
points = {} | ||
gate_needed = {} | ||
for q in qubit_list: | ||
points[q] = [] | ||
for _ in range(self.repetitions): | ||
if random.random() < self.p: | ||
add_gate = True | ||
gate_needed[q] = True | ||
points[q].append(1.0) | ||
else: | ||
points[q].append(0.0) | ||
|
||
if add_gate: | ||
moments.append(self._error_moment(parameter_number, | ||
qubit_list, gate_needed)) | ||
|
||
for q in qubit_list: | ||
if gate_needed[q]: | ||
sps = self._single_parameter_sweep(parameter_number, | ||
points[q]) | ||
sweep.sweep.factors[0].sweeps.extend([sps]) | ||
parameter_number += 1 | ||
|
||
if add_gate: | ||
new_job = Job(Circuit(moments), sweep) | ||
else: | ||
new_job = Job(Circuit(moments)) | ||
|
||
return new_job | ||
|
||
def _error_moment(self, current_parameter_number, qubit_list, gate_needed): | ||
"""Creates a moment of error gates. | ||
Args: | ||
current_parameter_number: The current number of error parameters | ||
already existing. Used for naming purposes. | ||
qubit_list: List of qubits to add error gates for. | ||
gate_needed: dictionary of qubits. Value is true if an error | ||
gate should be added for the qubit. | ||
Returns: | ||
a moment that includes parameterized errors gates. The | ||
parameters will be named with monotonically increasing error | ||
parameterized values. | ||
""" | ||
error_gates = [] | ||
for q in qubit_list: | ||
if gate_needed[q]: | ||
new_key = self._parameter_name + str(current_parameter_number) | ||
current_parameter_number += 1 | ||
new_parameter = ParameterizedValue(key=new_key) | ||
error_gates.append(xmon_gates.ExpZGate( | ||
half_turns=new_parameter).on(q)) | ||
return Moment(error_gates) | ||
|
||
def _single_parameter_sweep(self, current_parameter_number, point_list): | ||
"""Creates a single parameter sweep. | ||
Args: | ||
current_parameter_number: The current number of error parameters | ||
already existing. Used for naming parameterized values. | ||
point_list: list of floats to convert to SweepPoints | ||
Returns: | ||
a SingleParameterSweep for a given qubit based on randomized results. | ||
""" | ||
key = self._parameter_name + str(current_parameter_number) | ||
sps = SingleParameterSweep() | ||
sps.parameter_name = key | ||
for point in point_list: | ||
sps.sweep_points.points.append(point) | ||
return sps |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
# Copyright 2017 Google LLC | ||
# | ||
# 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 | ||
# | ||
# https://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. | ||
|
||
from cirq import circuits | ||
from cirq import ops | ||
from cirq.api.google.v1.params_pb2 import ParameterSweep | ||
from cirq.contrib.jobs import DepolarizerChannel | ||
from cirq.contrib.jobs import Job | ||
from cirq.google import xmon_gates | ||
from cirq.study.parameterized_value import ParameterizedValue | ||
|
||
|
||
def test_depolarizer_no_errors(): | ||
q1 = ops.QubitId() | ||
q2 = ops.QubitId() | ||
cnot = Job(circuits.Circuit([ | ||
circuits.Moment([ops.CNOT(q1, q2)]), | ||
])) | ||
noerrors = DepolarizerChannel(probability=0.0) | ||
|
||
assert noerrors.transform_job(cnot) == cnot | ||
|
||
|
||
def test_depolarizer_all_errors(): | ||
q1 = ops.QubitId() | ||
q2 = ops.QubitId() | ||
cnot = Job(circuits.Circuit([ | ||
circuits.Moment([ops.CNOT(q1, q2)]), | ||
])) | ||
allerrors = DepolarizerChannel(probability=1.0) | ||
p0 = ParameterizedValue(DepolarizerChannel._parameter_name + '0') | ||
p1 = ParameterizedValue(DepolarizerChannel._parameter_name + '1') | ||
|
||
sweep = ParameterSweep() | ||
sweep.repetitions = 1 | ||
sweep.sweep.factors.add() | ||
sweep.sweep.factors[0].sweeps.add() | ||
sweep.sweep.factors[0].sweeps.add() | ||
sweep.sweep.factors[0].sweeps[0].parameter_name = p0.key | ||
sweep.sweep.factors[0].sweeps[0].sweep_points.points.append(1.0) | ||
sweep.sweep.factors[0].sweeps[1].parameter_name = p1.key | ||
sweep.sweep.factors[0].sweeps[1].sweep_points.points.append(1.0) | ||
|
||
cnot_then_z = Job(circuits.Circuit([ | ||
circuits.Moment([ops.CNOT(q1, q2)]), | ||
circuits.Moment([xmon_gates.ExpZGate(half_turns=p0).on(q1), | ||
xmon_gates.ExpZGate(half_turns=p1).on(q2)])]), | ||
sweep) | ||
|
||
assert allerrors.transform_job(cnot) == cnot_then_z | ||
|
||
|
||
def test_depolarizer_multiple_repetitions(): | ||
q1 = ops.QubitId() | ||
q2 = ops.QubitId() | ||
cnot = Job(circuits.Circuit([ | ||
circuits.Moment([ops.CNOT(q1, q2)]), | ||
])) | ||
allerrors3 = DepolarizerChannel(probability=1.0, repetitions=3) | ||
p0 = ParameterizedValue(DepolarizerChannel._parameter_name + '0') | ||
p1 = ParameterizedValue(DepolarizerChannel._parameter_name + '1') | ||
|
||
sweep = ParameterSweep() | ||
sweep.repetitions = 1 | ||
sweep.sweep.factors.add() | ||
sweep.sweep.factors[0].sweeps.add() | ||
sweep.sweep.factors[0].sweeps.add() | ||
sweep.sweep.factors[0].sweeps[0].parameter_name = p0.key | ||
sweep.sweep.factors[0].sweeps[0].sweep_points.points.append(1.0) | ||
sweep.sweep.factors[0].sweeps[1].parameter_name = p1.key | ||
sweep.sweep.factors[0].sweeps[1].sweep_points.points.append(1.0) | ||
sweep.sweep.factors[0].sweeps[0].sweep_points.points.append(1.0) | ||
sweep.sweep.factors[0].sweeps[0].sweep_points.points.append(1.0) | ||
sweep.sweep.factors[0].sweeps[1].sweep_points.points.append(1.0) | ||
sweep.sweep.factors[0].sweeps[1].sweep_points.points.append(1.0) | ||
cnot_then_z3 = Job(circuits.Circuit([ | ||
circuits.Moment([ops.CNOT(q1, q2)]), | ||
circuits.Moment([xmon_gates.ExpZGate(half_turns=p0).on(q1), | ||
xmon_gates.ExpZGate(half_turns=p1).on(q2)])]), | ||
sweep) | ||
assert allerrors3.transform_job(cnot) == cnot_then_z3 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
# Copyright 2018 Google LLC | ||
# | ||
# 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 | ||
# | ||
# https://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. | ||
|
||
"""The data structure representing a quantum job. | ||
Job data contains, at minimum, contain a circuit and a parameter sweep of any | ||
parameters contained in the circuit. | ||
""" | ||
|
||
from cirq.api.google.v1.params_pb2 import ParameterSweep | ||
from cirq.circuits import Circuit | ||
|
||
|
||
class Job(object): | ||
"""A circuit coupled with any parameter sweeps and meta-data. | ||
This class should contain all information needed to submit to Quantum | ||
Engine. | ||
""" | ||
|
||
def __init__(self, circuit=Circuit(), sweep=ParameterSweep()): | ||
self.circuit = circuit | ||
self.sweep = sweep | ||
|
||
def __eq__(self, other): | ||
if not isinstance(other, type(self)): | ||
return NotImplemented | ||
return self.circuit == other.circuit and self.sweep == other.sweep | ||
|
||
def __ne__(self, other): | ||
return not self == other | ||
|
||
__hash__ = None | ||
|
||
def __repr__(self): | ||
return "Job(%s,%s)" % (self.circuit, self.sweep) | ||
|
||
def __str__(self): | ||
return "Job(%s,%s)" % (self.circuit, self.sweep) |
Oops, something went wrong.