tensorcircuit-nightly 1.2.1.dev20250725__py3-none-any.whl → 1.3.0.dev20250727__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of tensorcircuit-nightly might be problematic. Click here for more details.

tensorcircuit/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
- __version__ = "1.2.1.dev20250725"
1
+ __version__ = "1.3.0.dev20250727"
2
2
  __author__ = "TensorCircuit Authors"
3
3
  __creator__ = "refraction-ray"
4
4
 
@@ -442,7 +442,14 @@ class JaxBackend(jax_backend.JaxBackend, ExtendedBackend): # type: ignore
442
442
  def to_dlpack(self, a: Tensor) -> Any:
443
443
  import jax.dlpack
444
444
 
445
- return jax.dlpack.to_dlpack(a)
445
+ try:
446
+ return jax.dlpack.to_dlpack(a) # type: ignore
447
+ except AttributeError: # jax >v0.7
448
+ # jax.dlpack.to_dlpack was deprecated in JAX v0.6.0 and removed in JAX v0.7.0.
449
+ # Please use the newer DLPack API based on __dlpack__ and __dlpack_device__ instead.
450
+ # Typically, you can pass a JAX array directly to the `from_dlpack` function of
451
+ # another framework without using `to_dlpack`.
452
+ return a.__dlpack__()
446
453
 
447
454
  def set_random_state(
448
455
  self, seed: Optional[Union[int, PRNGKeyArray]] = None, get_only: bool = False
tensorcircuit/circuit.py CHANGED
@@ -231,6 +231,25 @@ class Circuit(BaseCircuit):
231
231
  pz: float,
232
232
  status: Optional[float] = None,
233
233
  ) -> float:
234
+ """
235
+ Apply a depolarizing channel to the circuit in a Monte Carlo way.
236
+ For each call, one of the Pauli gates (X, Y, Z) or an Identity gate is applied to the qubit
237
+ at the given index based on the probabilities `px`, `py`, and `pz`.
238
+
239
+ :param index: The index of the qubit to apply the depolarizing channel on.
240
+ :type index: int
241
+ :param px: The probability of applying an X gate.
242
+ :type px: float
243
+ :param py: The probability of applying a Y gate.
244
+ :type py: float
245
+ :param pz: The probability of applying a Z gate.
246
+ :type pz: float
247
+ :param status: A random number between 0 and 1 to determine which gate to apply. If None,
248
+ a random number is generated automatically. Defaults to None.
249
+ :type status: Optional[float], optional
250
+ :return: Returns 0.0. The function modifies the circuit in place.
251
+ :rtype: float
252
+ """
234
253
  if status is None:
235
254
  status = backend.implicit_randu()[0]
236
255
  g = backend.cond(
@@ -323,6 +342,35 @@ class Circuit(BaseCircuit):
323
342
  status: Optional[float] = None,
324
343
  name: Optional[str] = None,
325
344
  ) -> Tensor:
345
+ """
346
+ Apply a unitary Kraus channel to the circuit using a Monte Carlo approach. This method is functionally
347
+ similar to `unitary_kraus` but uses `backend.switch` for selecting the Kraus operator, which can have
348
+ different performance characteristics on some backends.
349
+
350
+ A random Kraus operator from the provided list is applied to the circuit based on the given probabilities.
351
+ This method is jittable and suitable for simulating noisy quantum circuits where the noise is represented
352
+ by unitary Kraus operators.
353
+
354
+ .. warning::
355
+ This method may have issues with `vmap` due to potential concurrent access locks, potentially related with
356
+ `backend.switch`. `unitary_kraus` is generally recommended.
357
+
358
+ :param kraus: A sequence of `Gate` objects representing the unitary Kraus operators.
359
+ :type kraus: Sequence[Gate]
360
+ :param index: The qubit indices on which to apply the Kraus channel.
361
+ :type index: int
362
+ :param prob: A sequence of probabilities corresponding to each Kraus operator. If None, probabilities
363
+ are derived from the operators themselves. Defaults to None.
364
+ :type prob: Optional[Sequence[float]], optional
365
+ :param status: A random number between 0 and 1 to determine which Kraus operator to apply. If None,
366
+ a random number is generated automatically. Defaults to None.
367
+ :type status: Optional[float], optional
368
+ :param name: An optional name for the operation. Defaults to None.
369
+ :type name: Optional[str], optional
370
+ :return: A tensor indicating which Kraus operator was applied.
371
+ :rtype: Tensor
372
+ """
373
+
326
374
  # dont use, has issue conflicting with vmap, concurrent access lock emerged
327
375
  # potential issue raised from switch
328
376
  # general impl from Monte Carlo trajectory depolarizing above
tensorcircuit/cons.py CHANGED
@@ -516,8 +516,8 @@ def _get_path_cache_friendly(
516
516
  nodes = list(nodes)
517
517
 
518
518
  nodes_new = sorted(nodes, key=lambda node: getattr(node, "_stable_id_", -1))
519
- if isinstance(algorithm, list):
520
- return algorithm, nodes_new
519
+ # if isinstance(algorithm, list):
520
+ # return algorithm, [nodes_new]
521
521
 
522
522
  all_edges = tn.get_all_edges(nodes_new)
523
523
  all_edges_sorted = sorted_edges(all_edges)
@@ -693,6 +693,51 @@ def _base(
693
693
  return final_node
694
694
 
695
695
 
696
+ class NodesReturn(Exception):
697
+ """
698
+ Intentionally stop execution to return a value.
699
+ """
700
+
701
+ def __init__(self, value_to_return: Any):
702
+ self.value = value_to_return
703
+ super().__init__(
704
+ f"Intentionally stopping execution to return: {value_to_return}"
705
+ )
706
+
707
+
708
+ def _get_sorted_nodes(nodes: List[Any], *args: Any, **kws: Any) -> Any:
709
+ nodes_new = sorted(nodes, key=lambda node: getattr(node, "_stable_id_", -1))
710
+ raise NodesReturn(nodes_new)
711
+
712
+
713
+ def function_nodes_capture(func: Callable[[Any], Any]) -> Callable[[Any], Any]:
714
+ @wraps(func)
715
+ def wrapper(*args: Any, **kwargs: Any) -> Any:
716
+ with runtime_contractor(method="before"):
717
+ try:
718
+ result = func(*args, **kwargs)
719
+ return result
720
+ except NodesReturn as e:
721
+ return e.value
722
+
723
+ return wrapper
724
+
725
+
726
+ @contextmanager
727
+ def runtime_nodes_capture(key: str = "nodes") -> Iterator[Any]:
728
+ old_contractor = getattr(thismodule, "contractor")
729
+ set_contractor(method="before")
730
+ captured_value: Dict[str, List[tn.Node]] = {}
731
+ try:
732
+ yield captured_value
733
+ except NodesReturn as e:
734
+ captured_value[key] = e.value
735
+ finally:
736
+ for module in sys.modules:
737
+ if module.startswith(package_name):
738
+ setattr(sys.modules[module], "contractor", old_contractor)
739
+
740
+
696
741
  def custom(
697
742
  nodes: List[Any],
698
743
  optimizer: Any,
@@ -763,6 +808,16 @@ def custom_stateful(
763
808
 
764
809
  # only work for custom
765
810
  def contraction_info_decorator(algorithm: Callable[..., Any]) -> Callable[..., Any]:
811
+ """Decorator to add contraction information logging to an optimizer.
812
+
813
+ This decorator wraps an optimization algorithm and prints detailed information
814
+ about the contraction cost (FLOPs, size, write) and path finding time.
815
+
816
+ :param algorithm: The optimization algorithm to decorate.
817
+ :type algorithm: Callable[..., Any]
818
+ :return: The decorated optimization algorithm.
819
+ :rtype: Callable[..., Any]
820
+ """
766
821
  from cotengra import ContractionTree
767
822
 
768
823
  def new_algorithm(
@@ -869,6 +924,9 @@ def set_contractor(
869
924
  **kws,
870
925
  )
871
926
 
927
+ elif method == "before": # a hack way to get the nodes
928
+ cf = _get_sorted_nodes
929
+
872
930
  else:
873
931
  # cf = getattr(tn.contractors, method, None)
874
932
  # if not cf: