Skip to content

Commit

Permalink
Merge pull request #125 from CQCL/feature/mainonly
Browse files Browse the repository at this point in the history
Feature/mainonly
  • Loading branch information
PabloAndresCQ authored Jun 6, 2024
2 parents b1ef075 + 8ef8ab4 commit ef0d2fe
Show file tree
Hide file tree
Showing 20 changed files with 633 additions and 148 deletions.
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

0 comments on commit ef0d2fe

Please sign in to comment.