Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/mainonly #125

Merged
merged 14 commits into from
Jun 6, 2024
Merged
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
1 change: 1 addition & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Please mention any github issues addressed by this PR.

# Checklist

- [ ] I have run the tests on a device with GPUs.
- [ ] I have performed a self-review of my code.
- [ ] I have commented hard-to-understand parts of my code.
- [ ] I have made corresponding changes to the public API documentation.
Expand Down
3 changes: 1 addition & 2 deletions .github/workflows/build_and_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@ on:
pull_request:
branches:
- main
- develop
push:
branches:
- develop
- main
- 'wheel/**'
- 'runci/**'
release:
Expand Down
1 change: 0 additions & 1 deletion .github/workflows/check-examples.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ name: check examples
on:
pull_request:
branches:
- develop
- main
schedule:
# 04:00 every Saturday morning
Expand Down
16 changes: 16 additions & 0 deletions .github/workflows/issue-to-project.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
name: Add issues to project

on:
issues:
types:
- opened

jobs:
add-to-project:
name: Add issue to project
runs-on: ubuntu-latest
steps:
- uses: actions/[email protected]
with:
project-url: https://github.com/orgs/CQCL-DEV/projects/19
github-token: ${{ secrets.ADD_TO_PROJECT_PAT }}
5 changes: 2 additions & 3 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@ on:
pull_request:
branches:
- main
- develop
push:
branches:
- develop
- main
- 'wheel/**'
- 'runci/**'

Expand All @@ -31,4 +30,4 @@ jobs:
black --check .
- name: Run pylint
run: |
pylint --recursive=y --ignore=ttn_tutorial.py,mps_tutorial.py */
pylint --recursive=y --ignore=ttn_tutorial.py,mps_tutorial.py */
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ pip install -e .
## Contributing

Pull requests are welcome. To make a PR, first fork the repo, make your proposed
changes on the `develop` branch, and open a PR from your fork. If it passes
changes on the `main` branch, and open a PR from your fork. If it passes
tests and is accepted after review, it will be merged in.

### Code style
Expand Down
9 changes: 9 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
Changelog
~~~~~~~~~

Unreleased
----------

* New feature: ``add_qubit`` to add fresh qubits at specified positions in an ``MPS``.
* New feature: added an option to ``measure`` to toggle destructive measurement on/off. Currently only supported for ``MPS``.
* New feature: a seed can now be provided to ``Config`` objects, providing reproducibility across ``StructuredState`` simulations.
* New feature: ``apply_unitary`` both for ``MPS`` and ``TTN`` to apply an arbitrary unitary matrix, rather than a ``pytket.Command``.
* New feature: ``apply_qubit_relabelling`` both for ``MPS`` and ``TTN`` to change the name of their qubits. This is now used within ``simulate`` to take into account the action of implicit SWAPs in pytket circuits (no additional SWAP gates are applied).

0.6.1 (April 2024)
------------------

Expand Down
6 changes: 5 additions & 1 deletion docs/modules/structured_state.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,17 @@ Simulation

.. autoclass:: pytket.extensions.cutensornet.structured_state.CuTensorNetHandle

.. automethod:: destroy


Classes
~~~~~~~

.. autoclass:: pytket.extensions.cutensornet.structured_state.StructuredState()

.. automethod:: __init__
.. automethod:: is_valid
.. automethod:: apply_gate
.. automethod:: apply_unitary
.. automethod:: apply_scalar
.. automethod:: vdot
.. automethod:: sample
Expand All @@ -49,10 +51,12 @@ Classes
.. autoclass:: pytket.extensions.cutensornet.structured_state.MPSxGate()

.. automethod:: __init__
.. automethod:: add_qubit

.. autoclass:: pytket.extensions.cutensornet.structured_state.MPSxMPO()

.. automethod:: __init__
.. automethod:: add_qubit


Miscellaneous
Expand Down
2 changes: 1 addition & 1 deletion lint-requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
black~=24.4
pylint~=3.1
pylint~=3.2
2 changes: 1 addition & 1 deletion pytket/extensions/cutensornet/general.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
def set_logger(
logger_name: str,
level: int = logging.WARNING,
fmt: str = "[%(asctime)s] %(name)s (%(levelname)s) - %(message)s",
fmt: str = "[%(asctime)s.%(msecs)03d] %(name)s (%(levelname)s) - %(message)s",
) -> Logger:
"""Initialises and configures a logger object.

Expand Down
82 changes: 73 additions & 9 deletions pytket/extensions/cutensornet/structured_state/general.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,20 @@ def __init__(self, device_id: Optional[int] = None):

self.handle = cutn.create()

def destroy(self) -> None:
"""Destroys the memory handle, releasing memory.

Only call this method if you are initialising a ``CuTensorNetHandle`` outside
a ``with CuTensorNetHandle() as libhandle`` statement.
"""
cutn.destroy(self.handle)
self._is_destroyed = True

def __enter__(self) -> CuTensorNetHandle:
return self

def __exit__(self, exc_type: Any, exc_value: Any, exc_tb: Any) -> None:
cutn.destroy(self.handle)
self._is_destroyed = True
self.destroy()


class Config:
Expand All @@ -79,6 +87,7 @@ def __init__(
self,
chi: Optional[int] = None,
truncation_fidelity: Optional[float] = None,
seed: Optional[int] = None,
float_precision: Type[Any] = np.float64,
value_of_zero: float = 1e-16,
leaf_size: int = 8,
Expand All @@ -102,6 +111,11 @@ def __init__(
``|<psi|phi>|^2 >= trucantion_fidelity``, where ``|psi>`` and ``|phi>``
are the states before and after truncation (both normalised).
If not provided, it will default to its maximum value 1.
seed: Seed for the random number generator. Setting a seed provides
reproducibility across simulations using ``StructuredState``, in the
sense that they will produce the same sequence of measurement outcomes.
Crucially, consecutive samples taken from the same ``StructuredState``
can still be different from each other.
float_precision: The floating point precision used in tensor calculations;
choose from ``numpy`` types: ``np.float64`` or ``np.float32``.
Complex numbers are represented using two of such
Expand Down Expand Up @@ -176,6 +190,8 @@ def __init__(
UserWarning,
)

self.seed = seed

if leaf_size >= 65: # Imposed to avoid bond ID collisions
# More than 20 qubits is already unreasonable for a leaf anyway
raise ValueError("Maximum allowed leaf_size is 65.")
Expand All @@ -191,6 +207,7 @@ def copy(self) -> Config:
return Config(
chi=self.chi,
truncation_fidelity=self.truncation_fidelity,
seed=self.seed,
float_precision=self._real_t, # type: ignore
value_of_zero=self.zero,
leaf_size=self.leaf_size,
Expand Down Expand Up @@ -224,7 +241,35 @@ def apply_gate(self, gate: Command) -> StructuredState:

Raises:
RuntimeError: If the ``CuTensorNetHandle`` is out of scope.
RuntimeError: If gate is not supported.
ValueError: If the command introduced is not a unitary gate.
ValueError: If gate acts on more than 2 qubits.
"""
raise NotImplementedError(f"Method not implemented in {type(self).__name__}.")

@abstractmethod
def apply_unitary(
self, unitary: cp.ndarray, qubits: list[Qubit]
) -> StructuredState:
"""Applies the unitary to the specified qubits of the StructuredState.

Note:
It is assumed that the matrix provided by the user is unitary. If this is
not the case, the program will still run, but its behaviour is undefined.

Args:
unitary: The matrix to be applied as a CuPy ndarray. It should either be
a 2x2 matrix if acting on one qubit or a 4x4 matrix if acting on two.
qubits: The qubits the unitary acts on. Only one qubit and two qubit
unitaries are supported.

Returns:
``self``, to allow for method chaining.

Raises:
RuntimeError: If the ``CuTensorNetHandle`` is out of scope.
ValueError: If the number of qubits provided is not one or two.
ValueError: If the size of the matrix does not match with the number of
qubits provided.
"""
raise NotImplementedError(f"Method not implemented in {type(self).__name__}.")

Expand All @@ -240,6 +285,24 @@ def apply_scalar(self, scalar: complex) -> StructuredState:
"""
raise NotImplementedError(f"Method not implemented in {type(self).__name__}.")

@abstractmethod
def apply_qubit_relabelling(self, qubit_map: dict[Qubit, Qubit]) -> StructuredState:
"""Relabels each qubit ``q`` as ``qubit_map[q]``.

This does not apply any SWAP gate, nor it changes the internal structure of the
state. It simply changes the label of the physical bonds of the tensor network.

Args:
qubit_map: Dictionary mapping each qubit to its new label.

Returns:
``self``, to allow for method chaining.

Raises:
ValueError: If any of the keys in ``qubit_map`` are not qubits in the state.
"""
raise NotImplementedError(f"Method not implemented in {type(self).__name__}.")

@abstractmethod
def vdot(self, other: StructuredState) -> complex:
"""Obtain the inner product of the two states: ``<self|other>``.
Expand Down Expand Up @@ -276,17 +339,18 @@ def sample(self) -> dict[Qubit, int]:
raise NotImplementedError(f"Method not implemented in {type(self).__name__}.")

@abstractmethod
def measure(self, qubits: set[Qubit]) -> dict[Qubit, int]:
"""Applies a Z measurement on ``qubits``, updates the state and returns outcome.
def measure(self, qubits: set[Qubit], destructive: bool = True) -> dict[Qubit, int]:
"""Applies a Z measurement on each of the ``qubits``.

Notes:
After applying this function, ``self`` will contain the projected
state over the non-measured qubits.

The resulting state has been normalised.
After applying this function, ``self`` will contain the normalised
projected state.

Args:
qubits: The subset of qubits to be measured.
destructive: If ``True``, the resulting state will not contain the
measured qubits. If ``False``, these qubits will appear on the
state corresponding to the measurement outcome. Defaults to ``True``.

Returns:
A dictionary mapping the given ``qubits`` to their measurement outcome,
Expand Down
Loading
Loading