Skip to content

Commit

Permalink
V4 Program API: Back GateDef with GateDefinition (#1549)
Browse files Browse the repository at this point in the history
* setup DefGate test suite

* add baseline tests for permutation and pauli gates

* compatibility for paulis, expressions, back DefGate with GateDefinition

* back DefPermutationGate with quil-rs

* back DefGateByPaulis w/ quil-rs

* better support Expressions

* dont try to support Expressions

* cleanup

* update tests

* combine int, float, and complex conversion
  • Loading branch information
MarquessV authored Mar 31, 2023
1 parent 5c56427 commit d5d7d38
Show file tree
Hide file tree
Showing 9 changed files with 480 additions and 173 deletions.
39 changes: 36 additions & 3 deletions pyquil/paulis.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,20 +38,25 @@
)

from pyquil.quilatom import (
Qubit,
QubitPlaceholder,
FormalArgument,
Expression,
ExpressionDesignator,
MemoryReference,
_convert_to_py_expression,
_convert_to_rs_expression,
)

import quil.instructions as quil_rs

from .quil import Program
from .gates import H, RZ, RX, CNOT, X, PHASE, QUANTUM_GATES
from numbers import Number, Complex
from collections import OrderedDict
import warnings

PauliTargetDesignator = Union[int, FormalArgument, QubitPlaceholder]
PauliTargetDesignator = Union[int, FormalArgument, Qubit, QubitPlaceholder]
PauliDesignator = Union["PauliTerm", "PauliSum"]

PAULI_OPS = ["X", "Y", "Z", "I"]
Expand Down Expand Up @@ -159,6 +164,17 @@ def __init__(
else:
self.coefficient = coefficient

@classmethod
def _from_rs_pauli_term(cls, term: quil_rs.PauliTerm) -> "PauliTerm":
term_list = [(str(gate), FormalArgument(arg)) for (gate, arg) in term.arguments]
coefficient = _convert_to_py_expression(term.expression.into_simplified())

return cls.from_list(term_list, coefficient)

def _to_rs_pauli_term(self) -> quil_rs.PauliTerm:
arguments = [(quil_rs.PauliGate.parse(gate), str(arg)) for arg, gate in self._ops.items()]
return quil_rs.PauliTerm(arguments, _convert_to_rs_expression(self.coefficient))

def id(self, sort_ops: bool = True) -> str:
"""
Returns an identifier string for the PauliTerm (ignoring the coefficient).
Expand Down Expand Up @@ -383,7 +399,9 @@ def compact_str(self) -> str:
return f"{self.coefficient}*{self.id(sort_ops=False)}"

@classmethod
def from_list(cls, terms_list: List[Tuple[str, int]], coefficient: float = 1.0) -> "PauliTerm":
def from_list(
cls, terms_list: List[Tuple[str, PauliTargetDesignator]], coefficient: ExpressionDesignator = 1.0
) -> "PauliTerm":
"""
Allocates a Pauli Term from a list of operators and indices. This is more efficient than
multiplying together individual terms.
Expand Down Expand Up @@ -569,6 +587,22 @@ def __init__(self, terms: Sequence[PauliTerm]):
else:
self.terms = terms

@classmethod
def _from_rs_pauli_sum(cls, pauli_sum: quil_rs.PauliSum) -> "PauliSum":
return cls([PauliTerm._from_rs_pauli_term(term) for term in pauli_sum.terms])

def _to_rs_pauli_sum(self, arguments: Optional[List[PauliTargetDesignator]] = None) -> quil_rs.PauliSum:
rs_arguments: List[str]
if arguments is None:
argument_set: Dict[str, None] = {}
for term_arguments in [term.get_qubits() for term in self.terms]:
argument_set.update({str(arg): None for arg in term_arguments})
rs_arguments = list(argument_set.keys())
else:
rs_arguments = [str(arg) for arg in arguments]
terms = [term._to_rs_pauli_term() for term in self.terms]
return quil_rs.PauliSum(rs_arguments, terms)

def __eq__(self, other: object) -> bool:
"""Equality testing to see if two PauliSum's are equivalent.
Expand Down Expand Up @@ -840,7 +874,6 @@ def commuting_sets(pauli_terms: PauliSum) -> List[List[PauliTerm]]:
isAssigned_bool = False
for p in range(m_s): # check if it commutes with each group
if isAssigned_bool is False:

if check_commutation(groups[p], pauli_terms.terms[j]):
isAssigned_bool = True
groups[p].append(pauli_terms.terms[j])
Expand Down
3 changes: 2 additions & 1 deletion pyquil/quil.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ def defined_gates(self) -> List[DefGate]:
"""
A list of defined gates on the program.
"""
return self._program.defined_gates
return [DefGate._from_rs_gate_definition(gate) for gate in self._program.defined_gates]

@property
def instructions(self) -> List[AbstractInstruction]:
Expand Down Expand Up @@ -230,6 +230,7 @@ def inst(self, *instructions: InstructionDesignator) -> "Program":
elif isinstance(instruction, RSProgram):
self._program += instruction
elif isinstance(instruction, AbstractInstruction):
print(instruction.out())
self.inst(RSProgram.parse(instruction.out()))
else:
raise ValueError("Invalid instruction: {}".format(instruction))
Expand Down
44 changes: 35 additions & 9 deletions pyquil/quilatom.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ def register(cls, n: int) -> List["QubitPlaceholder"]:
return [cls() for _ in range(n)]


QubitDesignator = Union[Qubit, QubitPlaceholder, FormalArgument, quil_rs.Qubit, int]
QubitDesignator = Union[Qubit, QubitPlaceholder, FormalArgument, int]


def _convert_to_rs_qubit(qubit: QubitDesignator) -> quil_rs.Qubit:
Expand All @@ -181,7 +181,7 @@ def _convert_to_rs_qubits(qubits: Iterable[QubitDesignator]) -> List[quil_rs.Qub
return [_convert_to_rs_qubit(qubit) for qubit in qubits]


def _convert_to_py_qubit(qubit: QubitDesignator) -> QubitDesignator:
def _convert_to_py_qubit(qubit: Union[QubitDesignator, quil_rs.Qubit]) -> QubitDesignator:
if isinstance(qubit, quil_rs.Qubit):
if qubit.is_fixed():
return Qubit(qubit.to_fixed())
Expand All @@ -192,7 +192,7 @@ def _convert_to_py_qubit(qubit: QubitDesignator) -> QubitDesignator:
raise ValueError(f"{type(qubit)} is not a valid QubitDesignator")


def _convert_to_py_qubits(qubits: Iterable[QubitDesignator]) -> List[QubitDesignator]:
def _convert_to_py_qubits(qubits: Iterable[Union[QubitDesignator, quil_rs.Qubit]]) -> List[QubitDesignator]:
return [_convert_to_py_qubit(qubit) for qubit in qubits]


Expand Down Expand Up @@ -309,18 +309,20 @@ def __hash__(self) -> int:
return hash(id(self))


ParameterDesignator = Union["Expression", "MemoryReference", quil_rs_expr.Expression, quil_rs.MemoryReference, Number]
ParameterDesignator = Union["Expression", "MemoryReference", Number, complex]


def _convert_to_rs_expression(parameter: ParameterDesignator) -> quil_rs_expr.Expression:
if isinstance(parameter, quil_rs_expr.Expression):
return parameter
elif isinstance(parameter, (Expression, MemoryReference, Number)):
elif isinstance(parameter, (int, float, complex)):
return quil_rs_expr.Expression.from_number(complex(parameter))
elif isinstance(parameter, (Expression, MemoryReference)):
return quil_rs_expr.Expression.parse(str(parameter))
raise ValueError(f"{type(parameter)} is not a valid ParameterDesignator")


def _convert_to_rs_expressions(parameters: Iterable[ParameterDesignator]) -> List[ParameterDesignator]:
def _convert_to_rs_expressions(parameters: Iterable[ParameterDesignator]) -> List[quil_rs_expr.Expression]:
return [_convert_to_rs_expression(parameter) for parameter in parameters]


Expand Down Expand Up @@ -392,7 +394,31 @@ def format_parameter(element: ParameterDesignator) -> str:


ExpressionValueDesignator = Union[int, float, complex]
ExpressionDesignator = Union["Expression", quil_rs_expr.Expression, ExpressionValueDesignator]
ExpressionDesignator = Union["Expression", ExpressionValueDesignator]


def _convert_to_py_expression(expression: Union[ExpressionDesignator, quil_rs_expr.Expression]) -> ExpressionDesignator:
if isinstance(expression, (Expression, Number)):
return expression
if isinstance(expression, quil_rs_expr.Expression):
if expression.is_pi():
return np.pi
if expression.is_number():
return expression.to_number()
if expression.is_variable():
return Parameter(expression.to_variable())
if expression.is_infix():
return BinaryExp._from_rs_infix_expression(expression.to_infix())
if expression.is_address():
return MemoryReference._from_rs_memory_reference(expression.to_address())
if expression.is_prefix():
prefix = expression.to_prefix()
py_expression = _convert_to_py_expression(prefix.expression)
if prefix == quil_rs_expr.PrefixOperator.Plus:
return py_expression
elif isinstance(py_expression, (int, float, complex, Expression)):
return -py_expression
raise TypeError(f"{type(expression)} is not a valid ExpressionDesignator")


class Expression(object):
Expand Down Expand Up @@ -575,8 +601,8 @@ def __init__(self, op1: ExpressionDesignator, op2: ExpressionDesignator):

@classmethod
def _from_rs_infix_expression(cls, infix_expression: quil_rs_expr.InfixExpression):
left = _convert_to_py_parameter(infix_expression.left)
right = _convert_to_py_parameter(infix_expression.right)
left = _convert_to_py_expression(infix_expression.left)
right = _convert_to_py_expression(infix_expression.right)
if infix_expression.operator == quil_rs_expr.InfixOperator.Plus:
return Add(left, right)
if infix_expression.operator == quil_rs_expr.InfixOperator.Minus:
Expand Down
Loading

0 comments on commit d5d7d38

Please sign in to comment.