Skip to content

Commit

Permalink
Add: Magic methods to Rust and Python
Browse files Browse the repository at this point in the history
- Add docstring to __init__.
- Add __iter__, __getitem__, __len__, __contains__, keys, values, and items methods to rust.
- Add equivalen methods to python + the __str__ method.
- Make description an optional attribute in rust.
- Other tweaks and fixes.
  • Loading branch information
raynelfss committed Apr 17, 2024
1 parent 2f5063e commit fc3b07e
Show file tree
Hide file tree
Showing 2 changed files with 156 additions and 20 deletions.
39 changes: 37 additions & 2 deletions crates/accelerate/src/target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,12 +124,13 @@ impl InstructionProperties {
}

type GateMapType = HashMap<String, Option<HashMap<Option<HashableVec<u32>>, Option<PyObject>>>>;
type TargetValue = Option<HashMap<Option<HashableVec<u32>>, Option<Py<PyAny>>>>;

#[pyclass(mapping, module = "qiskit._accelerate.target.Target")]
#[derive(Clone, Debug)]
pub struct Target {
#[pyo3(get, set)]
pub description: String,
pub description: Option<String>,
#[pyo3(get)]
pub num_qubits: Option<usize>,
#[pyo3(get)]
Expand Down Expand Up @@ -187,7 +188,7 @@ impl Target {
concurrent_measurements: Option<Vec<Vec<usize>>>,
) -> Self {
Target {
description: description.unwrap_or("".to_string()),
description,
num_qubits,
dt,
granularity: granularity.unwrap_or(1),
Expand Down Expand Up @@ -870,6 +871,7 @@ impl Target {
Vec::from_iter(0..self.num_qubits.unwrap_or_default())
}

// Class methods
#[classmethod]
fn from_configuration(
_cls: &Bound<'_, PyType>,
Expand Down Expand Up @@ -1192,6 +1194,39 @@ impl Target {
}
Ok(target)
}

// Magic methods:
fn __iter__(&self) -> PyResult<Vec<String>> {
Ok(self.gate_map.keys().cloned().collect())
}

fn __getitem__(&self, key: String) -> PyResult<TargetValue> {
if let Some(value) = self.gate_map.get(&key) {
Ok(value.to_owned())
} else {
Err(PyKeyError::new_err(format!("{key} not in gate_map")))
}
}

fn __len__(&self) -> PyResult<usize> {
Ok(self.gate_map.len())
}

fn __contains__(&self, item: String) -> PyResult<bool> {
Ok(self.gate_map.contains_key(&item))
}

fn keys(&self) -> PyResult<Vec<String>> {
Ok(self.gate_map.keys().cloned().collect())
}

fn values(&self) -> PyResult<Vec<TargetValue>> {
Ok(self.gate_map.values().cloned().collect())
}

fn items(&self) -> PyResult<Vec<(String, TargetValue)>> {
Ok(self.gate_map.clone().into_iter().collect())
}
}

#[pymodule]
Expand Down
137 changes: 119 additions & 18 deletions qiskit/transpiler/_target.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,26 +145,69 @@ def __repr__(self):
class Target:
def __init__(
self,
description=None,
num_qubits=0,
dt=None,
granularity=1,
min_length=1,
pulse_alignment=1,
acquire_alignment=1,
qubit_properties=None,
concurrent_measurements=None,
description: str | None = None,
num_qubits: int = 0,
dt: float | None = None,
granularity: int = 1,
min_length: int = 1,
pulse_alignment: int = 1,
acquire_alignment: int = 1,
qubit_properties: list | None = None,
concurrent_measurements: list | None = None,
):
"""
Create a new ``Target`` object
Args:
description (str): An optional string to describe the Target.
num_qubits (int): An optional int to specify the number of qubits
the backend target has. If not set it will be implicitly set
based on the qargs when :meth:`~qiskit.Target.add_instruction`
is called. Note this must be set if the backend target is for a
noiseless simulator that doesn't have constraints on the
instructions so the transpiler knows how many qubits are
available.
dt (float): The system time resolution of input signals in seconds
granularity (int): An integer value representing minimum pulse gate
resolution in units of ``dt``. A user-defined pulse gate should
have duration of a multiple of this granularity value.
min_length (int): An integer value representing minimum pulse gate
length in units of ``dt``. A user-defined pulse gate should be
longer than this length.
pulse_alignment (int): An integer value representing a time
resolution of gate instruction starting time. Gate instruction
should start at time which is a multiple of the alignment
value.
acquire_alignment (int): An integer value representing a time
resolution of measure instruction starting time. Measure
instruction should start at time which is a multiple of the
alignment value.
qubit_properties (list): A list of :class:`~.QubitProperties`
objects defining the characteristics of each qubit on the
target device. If specified the length of this list must match
the number of qubits in the target, where the index in the list
matches the qubit number the properties are defined for. If some
qubits don't have properties available you can set that entry to
``None``
concurrent_measurements(list): A list of sets of qubits that must be
measured together. This must be provided
as a nested list like ``[[0, 1], [2, 3, 4]]``.
Raises:
ValueError: If both ``num_qubits`` and ``qubit_properties`` are both
defined and the value of ``num_qubits`` differs from the length of
``qubit_properties``.
"""

self._Target = Target2(
description,
num_qubits,
dt,
granularity,
min_length,
pulse_alignment,
acquire_alignment,
qubit_properties,
concurrent_measurements,
description=description,
num_qubits=num_qubits,
dt=dt,
granularity=granularity,
min_length=min_length,
pulse_alignment=pulse_alignment,
acquire_alignment=acquire_alignment,
qubit_properties=qubit_properties,
concurrent_measurements=concurrent_measurements,
)

# Convert prior attributes into properties to get dynamically
Expand Down Expand Up @@ -910,3 +953,61 @@ def from_configuration(
custom_name_mapping,
)
return target

# Magic methods
def __iter__(self):
return iter(self._Target.__iter__())

def __getitem__(self, key):
return self._Target[key]

def __len__(self):
return len(self._Target)

def __contains__(self, item):
return item in self._Target

def keys(self):
return {x: None for x in self._Target.keys()}.keys()

def values(self):
return self._Target.values()

def items(self):
return self._Target.gate_map.items()

def __str__(self):
output = io.StringIO()
if self.description is not None:
output.write(f"Target: {self.description}\n")
else:
output.write("Target\n")
output.write(f"Number of qubits: {self.num_qubits}\n")
output.write("Instructions:\n")
for inst, qarg_props in self._Target.items():
output.write(f"\t{inst}\n")
for qarg, props in qarg_props.items():
if qarg is None:
continue
if props is None:
output.write(f"\t\t{qarg}\n")
continue
prop_str_pieces = [f"\t\t{qarg}:\n"]
duration = getattr(props, "duration", None)
if duration is not None:
prop_str_pieces.append(f"\t\t\tDuration: {duration} sec.\n")
error = getattr(props, "error", None)
if error is not None:
prop_str_pieces.append(f"\t\t\tError Rate: {error}\n")
schedule = getattr(props, "_calibration", None)
if schedule is not None:
prop_str_pieces.append("\t\t\tWith pulse schedule calibration\n")
extra_props = getattr(props, "properties", None)
if extra_props is not None:
extra_props_pieces = [
f"\t\t\t\t{key}: {value}\n" for key, value in extra_props.items()
]
extra_props_str = "".join(extra_props_pieces)
prop_str_pieces.append(f"\t\t\tExtra properties:\n{extra_props_str}\n")
output.write("".join(prop_str_pieces))
return output.getvalue()

0 comments on commit fc3b07e

Please sign in to comment.