Skip to content
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/test-lock.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
strategy:
matrix:
os: [ ubuntu-latest ]
python-version: [ "3.7", "3.8", "3.9", "3.10", "3.11" ]
python-version: [ "3.8", "3.9", "3.10", "3.11" ]
runs-on: ${{ matrix.os }}
steps:
- name: Check out OQpy
Expand All @@ -23,7 +23,7 @@ jobs:

- name: Install poetry
run: |
make install-poetry
make install-poetry poetry_version=1.5.1
poetry --version
poetry config virtualenvs.in-project true

Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
strategy:
matrix:
os: [ ubuntu-latest ]
python-version: [ "3.7", "3.8", "3.9", "3.10", "3.11" ]
python-version: [ "3.8", "3.9", "3.10", "3.11" ]
runs-on: ${{ matrix.os }}
steps:
- name: Check out OQpy
Expand Down Expand Up @@ -79,7 +79,7 @@ jobs:
strategy:
matrix:
os: [ ubuntu-latest ]
python-version: [ "3.7", "3.8", "3.9", "3.10", "3.11" ]
python-version: [ "3.8", "3.9", "3.10", "3.11" ]
runs-on: ${{ matrix.os }}
steps:
- name: Check out OQpy
Expand All @@ -93,7 +93,7 @@ jobs:

- name: Install poetry
run: |
make install-poetry
make install-poetry poetry_version=1.5.1
poetry --version
poetry config virtualenvs.in-project true

Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,4 @@ check-citation:
.PHONY: install-poetry
install-poetry:
command -v curl >/dev/null 2>&1 || { echo >&2 "please install curl and retry."; exit 1; }
curl -sSL https://install.python-poetry.org | python -
curl -sSL https://install.python-poetry.org | POETRY_VERSION=$(poetry_version) python -
17 changes: 10 additions & 7 deletions oqpy/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,23 @@
from __future__ import annotations

import math
import sys
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING, Any, Iterable, Optional, Sequence, Union
from typing import (
TYPE_CHECKING,
Any,
Iterable,
Optional,
Protocol,
Sequence,
Union,
runtime_checkable,
)

import numpy as np
from openpulse import ast

from oqpy import classical_types

if sys.version_info >= (3, 8):
from typing import Protocol, runtime_checkable
else:
from typing_extensions import Protocol, runtime_checkable

if TYPE_CHECKING:
from oqpy import Program

Expand Down
44 changes: 24 additions & 20 deletions oqpy/classical_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@
"ComplexVar",
"DurationVar",
"OQFunctionCall",
"OQIndexExpression",
"StretchVar",
"_ClassicalVar",
"duration",
Expand Down Expand Up @@ -236,6 +235,25 @@ def __init__(self, *args: Any, size: int | None = None, **kwargs: Any):
self.size = size
super().__init__(*args, **kwargs, size=ast.IntegerLiteral(self.size) if self.size else None)

def _validate_getitem_index(self, index: AstConvertible) -> None:
"""Validate the index and variable for `__getitem__`.

Args:
var (_SizedVar): Variable to apply `__getitem__`.
index (AstConvertible): Index for `__getitem__`.
"""
if self.size is None:
raise TypeError(f"'{self.name}' is not subscriptable")

if isinstance(index, int):
if not 0 <= index < self.size:
raise IndexError("list index out of range.")
elif isinstance(index, OQPyExpression):
if not isinstance(index.type, (ast.IntType, ast.UintType)):
raise IndexError("The list index must be an integer.")
else:
raise IndexError("The list index must be an integer.")


_SizedVarT = TypeVar("_SizedVarT", bound=_SizedVar)

Expand Down Expand Up @@ -274,7 +292,8 @@ class BitVar(_SizedVar):
type_cls = ast.BitType

def __getitem__(self, index: AstConvertible) -> OQIndexExpression:
return OQIndexExpression(collection=self, index=index)
self._validate_getitem_index(index)
return OQIndexExpression(collection=self, index=index, type_=self.type_cls())


class ComplexVar(_ClassicalVar):
Expand Down Expand Up @@ -380,31 +399,16 @@ def __init__(
)

def __getitem__(self, index: AstConvertible) -> OQIndexExpression:
return OQIndexExpression(collection=self, index=index)
return OQIndexExpression(collection=self, index=index, type_=self.base_type().type_cls())


class OQIndexExpression(OQPyExpression):
"""An oqpy expression corresponding to an index expression."""

def __init__(self, collection: AstConvertible, index: AstConvertible):
def __init__(self, collection: AstConvertible, index: AstConvertible, type_: ast.ClassicalType):
self.collection = collection
self.index = index

if isinstance(collection, ArrayVar):
self.type = collection.base_type().type_cls()

if isinstance(collection, _SizedVar):
if collection.size is None:
raise TypeError(f"'{collection.type_cls}' object is not subscriptable")

if isinstance(self.index, int):
if not 0 <= self.index < collection.size:
raise IndexError("list index out of range.")
elif isinstance(self.index, OQPyExpression):
if not isinstance(self.index.type, (ast.IntType, ast.UintType)):
raise IndexError("The list index must be an integer.")
else:
raise IndexError("The list index must be an integer.")
self.type = type_

def to_ast(self, program: Program) -> ast.IndexExpression:
"""Converts this oqpy index expression into an ast node."""
Expand Down
10 changes: 9 additions & 1 deletion oqpy/program.py
Original file line number Diff line number Diff line change
Expand Up @@ -483,7 +483,7 @@ def gate(
self, qubits: AstConvertible | Iterable[AstConvertible], name: str, *args: Any
) -> Program:
"""Apply a gate to a qubit or set of qubits."""
if isinstance(qubits, quantum_types.Qubit):
if isinstance(qubits, (quantum_types.Qubit, quantum_types.IndexedQubitArray)):
qubits = [qubits]
assert isinstance(qubits, Iterable)
self._add_statement(
Expand Down Expand Up @@ -523,6 +523,14 @@ def pragma(self, command: str) -> Program:
self._add_statement(ast.Pragma(command))
return self

def include(self, path: str) -> Program:
"""Add an include statement."""
if len(self.stack) != 1:
# cf. https://openqasm.com/language/comments.html#included-files
raise RuntimeError("Include statements must be global")
self._add_statement(ast.Include(path))
return self

def _do_assignment(self, var: AstConvertible, op: str, value: AstConvertible) -> None:
"""Helper function for variable assignment operations."""
if isinstance(var, classical_types.DurationVar):
Expand Down
29 changes: 24 additions & 5 deletions oqpy/quantum_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
from oqpy.program import Program


__all__ = ["Qubit", "QubitArray", "defcal", "gate", "PhysicalQubits", "Cal"]
__all__ = ["Qubit", "defcal", "gate", "PhysicalQubits", "Cal"]


class Qubit(Var):
Expand All @@ -39,11 +39,13 @@ class Qubit(Var):
def __init__(
self,
name: str,
size: Optional[int] = None,
needs_declaration: bool = True,
annotations: Sequence[str | tuple[str, str]] = (),
):
super().__init__(name, needs_declaration=needs_declaration)
self.name = name
self.size = size
self.annotations = annotations

def to_ast(self, prog: Program) -> ast.Expression:
Expand All @@ -53,10 +55,18 @@ def to_ast(self, prog: Program) -> ast.Expression:

def make_declaration_statement(self, program: Program) -> ast.Statement:
"""Make an ast statement that declares the OQpy variable."""
decl = ast.QubitDeclaration(ast.Identifier(self.name), size=None)
decl = ast.QubitDeclaration(
ast.Identifier(self.name),
size=ast.IntegerLiteral(self.size) if self.size else self.size,
)
decl.annotations = make_annotations(self.annotations)
return decl

def __getitem__(self, index: AstConvertible) -> IndexedQubitArray:
if self.size is None:
raise TypeError(f"'{self.name}' is not subscriptable")
return IndexedQubitArray(collection=self, index=index)


class PhysicalQubits:
"""Provides a means of accessing qubit variables corresponding to physical qubits.
Expand All @@ -68,9 +78,18 @@ def __class_getitem__(cls, item: int) -> Qubit:
return Qubit(f"${item}", needs_declaration=False)


# Todo (#51): support QubitArray
class QubitArray:
"""Represents an array of qubits."""
class IndexedQubitArray:
"""Represents an indexed qubit array."""

def __init__(self, collection: Qubit, index: AstConvertible):
self.collection = collection
self.index = index

def to_ast(self, program: Program) -> ast.IndexExpression:
"""Converts this indexed qubit array into an ast node."""
return ast.IndexExpression(
collection=to_ast(program, self.collection), index=[to_ast(program, self.index)]
)


@contextlib.contextmanager
Expand Down
Loading