Skip to content

Commit

Permalink
Merge remote-tracking branch 'ibm/main' into static-setup
Browse files Browse the repository at this point in the history
  • Loading branch information
jakelishman committed Nov 29, 2023
2 parents 927540b + 3556740 commit 338a0e7
Show file tree
Hide file tree
Showing 71 changed files with 336 additions and 4,401 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ It also contains a transpiler that supports optimizing quantum circuits and a qu

For more details on how to use Qiskit, refer to the documentation located here:

<https://qiskit.org/documentation/>
<https://docs.quantum.ibm.com/>


## Installation
Expand Down
28 changes: 16 additions & 12 deletions crates/accelerate/src/euler_one_qubit_decomposer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -380,22 +380,26 @@ fn circuit_rr(
if !simplify {
atol = -1.0;
}
if theta.abs() < atol && phi.abs() < atol && lam.abs() < atol {
return OneQubitGateSequence {
gates: circuit,
global_phase: phase,
};
}
if (theta - PI).abs() > atol {

if mod_2pi((phi + lam) / 2., atol).abs() < atol {
// This can be expressed as a single R gate
if theta.abs() > atol {
circuit.push((String::from("r"), vec![theta, mod_2pi(PI / 2. + phi, atol)]));
}
} else {
// General case: use two R gates
if (theta - PI).abs() > atol {
circuit.push((
String::from("r"),
vec![theta - PI, mod_2pi(PI / 2. - lam, atol)],
));
}
circuit.push((
String::from("r"),
vec![theta - PI, mod_2pi(PI / 2. - lam, atol)],
vec![PI, mod_2pi(0.5 * (phi - lam + PI), atol)],
));
}
circuit.push((
String::from("r"),
vec![PI, mod_2pi(0.5 * (phi - lam + PI), atol)],
));

OneQubitGateSequence {
gates: circuit,
global_phase: phase,
Expand Down
36 changes: 24 additions & 12 deletions crates/accelerate/src/sabre_swap/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,23 +171,35 @@ fn populate_extended_set(
let mut decremented: IndexMap<usize, u32, ahash::RandomState> =
IndexMap::with_hasher(ahash::RandomState::default());
let mut i = 0;
let mut visit_now: Vec<NodeIndex> = Vec::new();
while i < to_visit.len() && extended_set.len() < EXTENDED_SET_SIZE {
for edge in dag.dag.edges_directed(to_visit[i], Direction::Outgoing) {
let successor_node = edge.target();
let successor_index = successor_node.index();
*decremented.entry(successor_index).or_insert(0) += 1;
required_predecessors[successor_index] -= 1;
if required_predecessors[successor_index] == 0 {
if !dag.dag[successor_node].directive
&& !dag.node_blocks.contains_key(&successor_index)
{
if let [a, b] = dag.dag[successor_node].qubits[..] {
extended_set.push([a.to_phys(layout), b.to_phys(layout)]);
// Visit runs of non-2Q gates fully before moving on to children
// of 2Q gates. This way, traversal order is a BFS of 2Q gates rather
// than of all gates.
visit_now.push(to_visit[i]);
let mut j = 0;
while let Some(node) = visit_now.get(j) {
for edge in dag.dag.edges_directed(*node, Direction::Outgoing) {
let successor_node = edge.target();
let successor_index = successor_node.index();
*decremented.entry(successor_index).or_insert(0) += 1;
required_predecessors[successor_index] -= 1;
if required_predecessors[successor_index] == 0 {
if !dag.dag[successor_node].directive
&& !dag.node_blocks.contains_key(&successor_index)
{
if let [a, b] = dag.dag[successor_node].qubits[..] {
extended_set.push([a.to_phys(layout), b.to_phys(layout)]);
to_visit.push(successor_node);
continue;
}
}
visit_now.push(successor_node);
}
to_visit.push(successor_node);
}
j += 1;
}
visit_now.clear();
i += 1;
}
for (node, amount) in decremented.iter() {
Expand Down
1 change: 0 additions & 1 deletion docs/apidoc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ API Reference
primitives
qasm2
qasm3
qasm
qobj
qpy
quantum_info
Expand Down
6 changes: 0 additions & 6 deletions docs/apidoc/qasm.rst

This file was deleted.

1 change: 0 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ visualization = [
"Pillow >= 4.2.1",
"pylatexenc >= 1.4",
"seaborn >= 0.9.0",
"pygments >= 2.4",
]
crosstalk-pass = [
"z3-solver >= 4.7",
Expand Down
15 changes: 12 additions & 3 deletions qiskit/circuit/classical/expr/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,11 @@
These objects are mutable and should not be reused in a different location without a copy.
The entry point from general circuit objects to the expression system is by wrapping the object
in a :class:`Var` node and associating a :class:`~.types.Type` with it.
The base for dynamic variables is the :class:`Var`, which can be either an arbitrarily typed runtime
variable, or a wrapper around a :class:`.Clbit` or :class:`.ClassicalRegister`.
.. autoclass:: Var
:members: var, name
Similarly, literals used in comparison (such as integers) should be lifted to :class:`Value` nodes
with associated types.
Expand Down Expand Up @@ -86,10 +87,18 @@
The functions and methods described in this section are a more user-friendly way to build the
expression tree, while staying close to the internal representation. All these functions will
automatically lift valid Python scalar values into corresponding :class:`Var` or :class:`Value`
objects, and will resolve any required implicit casts on your behalf.
objects, and will resolve any required implicit casts on your behalf. If you want to directly use
some scalar value as an :class:`Expr` node, you can manually :func:`lift` it yourself.
.. autofunction:: lift
Typically you should create memory-owning :class:`Var` instances by using the
:meth:`.QuantumCircuit.add_var` method to declare them in some circuit context, since a
:class:`.QuantumCircuit` will not accept an :class:`Expr` that contains variables that are not
already declared in it, since it needs to know how to allocate the storage and how the variable will
be initialized. However, should you want to do this manually, you should use the low-level
:meth:`Var.new` call to safely generate a named variable for usage.
You can manually specify casts in cases where the cast is allowed in explicit form, but may be
lossy (such as the cast of a higher precision :class:`~.types.Uint` to a lower precision one).
Expand Down
43 changes: 38 additions & 5 deletions qiskit/circuit/classical/expr/expr.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import abc
import enum
import typing
import uuid

from .. import types

Expand Down Expand Up @@ -108,24 +109,56 @@ def __repr__(self):

@typing.final
class Var(Expr):
"""A classical variable."""
"""A classical variable.
__slots__ = ("var",)
These variables take two forms: a new-style variable that owns its storage location and has an
associated name; and an old-style variable that wraps a :class:`.Clbit` or
:class:`.ClassicalRegister` instance that is owned by some containing circuit. In general,
construction of variables for use in programs should use :meth:`Var.new` or
:meth:`.QuantumCircuit.add_var`."""

__slots__ = ("var", "name")

def __init__(
self, var: qiskit.circuit.Clbit | qiskit.circuit.ClassicalRegister, type: types.Type
self,
var: qiskit.circuit.Clbit | qiskit.circuit.ClassicalRegister | uuid.UUID,
type: types.Type,
*,
name: str | None = None,
):
self.type = type
self.var = var
"""A reference to the backing data storage of the :class:`Var` instance. When lifting
old-style :class:`.Clbit` or :class:`.ClassicalRegister` instances into a :class:`Var`,
this is exactly the :class:`.Clbit` or :class:`.ClassicalRegister`. If the variable is a
new-style classical variable (one that owns its own storage separate to the old
:class:`.Clbit`/:class:`.ClassicalRegister` model), this field will be a :class:`~uuid.UUID`
to uniquely identify it."""
self.name = name
"""The name of the variable. This is required to exist if the backing :attr:`var` attribute
is a :class:`~uuid.UUID`, i.e. if it is a new-style variable, and must be ``None`` if it is
an old-style variable."""

@classmethod
def new(cls, name: str, type: types.Type) -> typing.Self:
"""Generate a new named variable that owns its own backing storage."""
return cls(uuid.uuid4(), type, name=name)

def accept(self, visitor, /):
return visitor.visit_var(self)

def __eq__(self, other):
return isinstance(other, Var) and self.type == other.type and self.var == other.var
return (
isinstance(other, Var)
and self.type == other.type
and self.var == other.var
and self.name == other.name
)

def __repr__(self):
return f"Var({self.var}, {self.type})"
if self.name is None:
return f"Var({self.var}, {self.type})"
return f"Var({self.var}, {self.type}, name='{self.name}')"


@typing.final
Expand Down
5 changes: 0 additions & 5 deletions qiskit/circuit/library/blueprintcircuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,11 +122,6 @@ def parameters(self) -> ParameterView:
self._build()
return super().parameters

def qasm(self, formatted=False, filename=None, encoding=None):
if not self._is_built:
self._build()
return super().qasm(formatted, filename, encoding)

def _append(self, instruction, _qargs=None, _cargs=None):
if not self._is_built:
self._build()
Expand Down
6 changes: 6 additions & 0 deletions qiskit/circuit/parameterexpression.py
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,12 @@ def __truediv__(self, other):
def __rtruediv__(self, other):
return self._apply_operation(operator.truediv, other, reflected=True)

def __pow__(self, other):
return self._apply_operation(pow, other)

def __rpow__(self, other):
return self._apply_operation(pow, other, reflected=True)

def _call(self, ufunc):
return ParameterExpression(self._parameter_symbols, ufunc(self._symbol_expr))

Expand Down
60 changes: 0 additions & 60 deletions qiskit/circuit/quantumcircuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@
from qiskit.circuit.gate import Gate
from qiskit.circuit.parameter import Parameter
from qiskit.circuit.exceptions import CircuitError
from qiskit.utils import optionals as _optionals
from qiskit.utils.deprecation import deprecate_func
from . import _classical_resource_map
from ._utils import sort_parameters
Expand Down Expand Up @@ -1619,65 +1618,6 @@ def decompose(
# do not copy operations, this is done in the conversion with circuit_to_dag
return dag_to_circuit(dag, copy_operations=False)

def qasm(
self,
formatted: bool = False,
filename: str | None = None,
encoding: str | None = None,
) -> str | None:
"""Return OpenQASM 2.0 string.
.. seealso::
:func:`.qasm2.dump` and :func:`.qasm2.dumps`
The preferred entry points to the OpenQASM 2 export capabilities. These match the
interface for other serialisers in Qiskit.
Args:
formatted (bool): Return formatted OpenQASM 2.0 string.
filename (str): Save OpenQASM 2.0 to file with name 'filename'.
encoding (str): Optionally specify the encoding to use for the
output file if ``filename`` is specified. By default this is
set to the system's default encoding (ie whatever
``locale.getpreferredencoding()`` returns) and can be set to
any valid codec or alias from stdlib's
`codec module <https://docs.python.org/3/library/codecs.html#standard-encodings>`__
Returns:
str: If formatted=False.
Raises:
MissingOptionalLibraryError: If pygments is not installed and ``formatted`` is
``True``.
QASM2ExportError: If circuit has free parameters.
QASM2ExportError: If an operation that has no OpenQASM 2 representation is encountered.
"""
from qiskit import qasm2 # pylint: disable=cyclic-import

out = qasm2.dumps(self)
if filename is not None:
with open(filename, "w+", encoding=encoding) as file:
print(out, file=file)

if formatted:
_optionals.HAS_PYGMENTS.require_now("formatted OpenQASM 2.0 output")

import pygments
from pygments.formatters import ( # pylint: disable=no-name-in-module
Terminal256Formatter,
)
from qiskit.qasm.pygments import OpenQASMLexer
from qiskit.qasm.pygments import QasmTerminalStyle

code = pygments.highlight(
out, OpenQASMLexer(), Terminal256Formatter(style=QasmTerminalStyle)
)
print(code)
return None
# The old `QuantumCircuit.qasm()` method included a terminating new line that `qasm2.dumps`
# doesn't, so for full compatibility we add it back here.
return out + "\n"

def draw(
self,
output: str | None = None,
Expand Down
2 changes: 0 additions & 2 deletions qiskit/converters/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
.. autofunction:: dag_to_circuit
.. autofunction:: circuit_to_instruction
.. autofunction:: circuit_to_gate
.. autofunction:: ast_to_dag
.. autofunction:: dagdependency_to_circuit
.. autofunction:: circuit_to_dagdependency
.. autofunction:: dag_to_dagdependency
Expand All @@ -32,7 +31,6 @@
from .dag_to_circuit import dag_to_circuit
from .circuit_to_instruction import circuit_to_instruction
from .circuit_to_gate import circuit_to_gate
from .ast_to_dag import ast_to_dag
from .circuit_to_dagdependency import circuit_to_dagdependency
from .dagdependency_to_circuit import dagdependency_to_circuit
from .dag_to_dagdependency import dag_to_dagdependency
Expand Down
Loading

0 comments on commit 338a0e7

Please sign in to comment.