Skip to content

Commit

Permalink
Changes suggested by Iakov.
Browse files Browse the repository at this point in the history
  • Loading branch information
PabloAndresCQ committed Nov 7, 2023
1 parent 405f951 commit 5d4edf2
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 149 deletions.
2 changes: 1 addition & 1 deletion docs/modules/tnstate.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Simulation

.. autofunction:: pytket.extensions.cutensornet.tnstate.simulate

.. autoenum:: pytket.extensions.cutensornet.tnstate.ContractionAlg()
.. autoenum:: pytket.extensions.cutensornet.tnstate.SimulationAlgorithm()
:members:

.. autoclass:: pytket.extensions.cutensornet.tnstate.Config()
Expand Down
2 changes: 1 addition & 1 deletion pytket/extensions/cutensornet/tnstate/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"""

from .general import CuTensorNetHandle, Config, TNState
from .simulation import ContractionAlg, simulate, prepare_circuit_mps
from .simulation import SimulationAlgorithm, simulate, prepare_circuit_mps

from .mps import DirMPS, MPS
from .mps_gate import MPSxGate
Expand Down
30 changes: 4 additions & 26 deletions pytket/extensions/cutensornet/tnstate/general.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,10 @@ def __init__(
UserWarning,
)

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.")

self.leaf_size = leaf_size
self.k = k
self.optim_delta = 1e-5
Expand Down Expand Up @@ -394,29 +398,3 @@ def copy(self) -> TNState:
@abstractmethod
def _flush(self) -> None:
raise NotImplementedError(f"Method not implemented in {type(self).__name__}.")


def _safe_qr(
libhandle: CuTensorNetHandle, indices: str, T: cp.ndarray
) -> tuple[cp.ndarray, cp.ndarray]:
"""Wrapper of cuTensorNet QR decomposition to work around a bug in 23.6.0.
The bug has been reported https://github.com/NVIDIA/cuQuantum/discussions/96.
"""
try:
Q, R = tensor.decompose(
indices,
T,
method=tensor.QRMethod(),
options={"handle": libhandle.handle, "device_id": libhandle.device_id},
)
except cutn.cuTensorNetError:
Q, S, R = tensor.decompose(
indices,
T,
method=tensor.SVDMethod(partition="U"), # Contracts S to Q
options={"handle": libhandle.handle, "device_id": libhandle.device_id},
)
assert S is None

return (Q, R)
32 changes: 16 additions & 16 deletions pytket/extensions/cutensornet/tnstate/mps.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,24 +93,24 @@ def __init__(

n_tensors = len(qubits)
if n_tensors == 0: # There's no initialisation to be done
return None
pass
elif n_tensors == 1:
raise ValueError("Please, provide at least two qubits.")

self.qubit_position = {q: i for i, q in enumerate(qubits)}

# Create the list of tensors
self.tensors: list[Tensor] = []
self.canonical_form = {i: None for i in range(n_tensors)}

# Append each of the tensors initialised in state |0>
m_shape = (1, 1, 2) # Two virtual bonds (dim=1) and one physical
for i in range(n_tensors):
m_tensor = cp.empty(m_shape, dtype=self._cfg._complex_t)
# Initialise the tensor to ket 0
m_tensor[0][0][0] = 1
m_tensor[0][0][1] = 0
self.tensors.append(m_tensor)
else:
self.qubit_position = {q: i for i, q in enumerate(qubits)}

# Create the list of tensors
self.tensors: list[Tensor] = []
self.canonical_form = {i: None for i in range(n_tensors)}

# Append each of the tensors initialised in state |0>
m_shape = (1, 1, 2) # Two virtual bonds (dim=1) and one physical
for i in range(n_tensors):
m_tensor = cp.empty(m_shape, dtype=self._cfg._complex_t)
# Initialise the tensor to ket 0
m_tensor[0][0][0] = 1
m_tensor[0][0][1] = 0
self.tensors.append(m_tensor)

def is_valid(self) -> bool:
"""Verify that the MPS object is valid.
Expand Down
25 changes: 14 additions & 11 deletions pytket/extensions/cutensornet/tnstate/simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
from .ttn_gate import TTNxGate


class ContractionAlg(Enum):
class SimulationAlgorithm(Enum):
"""An enum to refer to the TNState contraction algorithm.
Each enum value corresponds to the class with the same name; see its docs for
Expand All @@ -48,7 +48,7 @@ class ContractionAlg(Enum):
def simulate(
libhandle: CuTensorNetHandle,
circuit: Circuit,
algorithm: ContractionAlg,
algorithm: SimulationAlgorithm,
config: Config,
) -> TNState:
"""Simulates the circuit and returns the ``TNState`` representing the final state.
Expand All @@ -65,7 +65,7 @@ def simulate(
libhandle: The cuTensorNet library handle that will be used to carry out
tensor operations.
circuit: The pytket circuit to be simulated.
algorithm: Choose between the values of the ``ContractionAlg`` enum.
algorithm: Choose between the values of the ``SimulationAlgorithm`` enum.
config: The configuration object for simulation.
Returns:
Expand All @@ -77,23 +77,23 @@ def simulate(
logger.info(
"Ordering the gates in the circuit to reduce canonicalisation overhead."
)
if algorithm == ContractionAlg.MPSxGate:
if algorithm == SimulationAlgorithm.MPSxGate:
tnstate = MPSxGate( # type: ignore
libhandle,
circuit.qubits,
config,
)
sorted_gates = _get_sorted_gates(circuit, algorithm)

elif algorithm == ContractionAlg.MPSxMPO:
elif algorithm == SimulationAlgorithm.MPSxMPO:
tnstate = MPSxMPO( # type: ignore
libhandle,
circuit.qubits,
config,
)
sorted_gates = _get_sorted_gates(circuit, algorithm)

elif algorithm == ContractionAlg.TTNxGate:
elif algorithm == SimulationAlgorithm.TTNxGate:
qubit_partition = _get_qubit_partition(circuit, config.leaf_size)
tnstate = TTNxGate( # type: ignore
libhandle,
Expand Down Expand Up @@ -226,7 +226,7 @@ def _get_qubit_partition(

def _get_sorted_gates(
circuit: Circuit,
algorithm: ContractionAlg,
algorithm: SimulationAlgorithm,
qubit_partition: Optional[dict[int, list[Qubit]]] = None,
) -> list[Command]:
"""Sorts the list of gates so that there's less canonicalisation during simulation.
Expand All @@ -251,7 +251,7 @@ def _get_sorted_gates(
remaining = set(range(len(all_gates)))

# Do some precomputation depending on the algorithm
if algorithm in [ContractionAlg.TTNxGate]:
if algorithm in [SimulationAlgorithm.TTNxGate]:
if qubit_partition is None:
raise RuntimeError("You must provide a qubit partition!")

Expand All @@ -260,7 +260,7 @@ def _get_sorted_gates(
for q in qubits:
leaf_of_qubit[q] = leaf

elif algorithm in [ContractionAlg.MPSxGate, ContractionAlg.MPSxMPO]:
elif algorithm in [SimulationAlgorithm.MPSxGate, SimulationAlgorithm.MPSxMPO]:
idx_of_qubit = {q: i for i, q in enumerate(circuit.qubits)}

else:
Expand Down Expand Up @@ -301,14 +301,17 @@ def _get_sorted_gates(
gate_qubits = all_gates[gate_idx].qubits

# Criterion for distance depends on the simulation algorithm
if algorithm in [ContractionAlg.TTNxGate]:
if algorithm in [SimulationAlgorithm.TTNxGate]:
gate_distance[gate_idx] = max( # Max common ancestor distance
leaf_of_qubit[last_qubits[0]] ^ leaf_of_qubit[gate_qubits[0]],
leaf_of_qubit[last_qubits[0]] ^ leaf_of_qubit[gate_qubits[1]],
leaf_of_qubit[last_qubits[1]] ^ leaf_of_qubit[gate_qubits[0]],
leaf_of_qubit[last_qubits[1]] ^ leaf_of_qubit[gate_qubits[1]],
)
elif algorithm in [ContractionAlg.MPSxGate, ContractionAlg.MPSxMPO]:
elif algorithm in [
SimulationAlgorithm.MPSxGate,
SimulationAlgorithm.MPSxMPO,
]:
gate_distance[gate_idx] = max( # Max linear distance between qubits
abs(idx_of_qubit[last_qubits[0]] - idx_of_qubit[gate_qubits[0]]),
abs(idx_of_qubit[last_qubits[0]] - idx_of_qubit[gate_qubits[1]]),
Expand Down
Loading

0 comments on commit 5d4edf2

Please sign in to comment.