qadence 1.8.0__tar.gz → 1.9.1__tar.gz

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.
Files changed (164) hide show
  1. {qadence-1.8.0 → qadence-1.9.1}/PKG-INFO +3 -4
  2. {qadence-1.8.0 → qadence-1.9.1}/mkdocs.yml +4 -2
  3. {qadence-1.8.0 → qadence-1.9.1}/pyproject.toml +3 -3
  4. {qadence-1.8.0 → qadence-1.9.1}/qadence/__init__.py +1 -1
  5. {qadence-1.8.0 → qadence-1.9.1}/qadence/analog/parse_analog.py +1 -2
  6. {qadence-1.8.0 → qadence-1.9.1}/qadence/backends/gpsr.py +8 -2
  7. {qadence-1.8.0 → qadence-1.9.1}/qadence/backends/pulser/backend.py +7 -23
  8. {qadence-1.8.0 → qadence-1.9.1}/qadence/backends/pyqtorch/backend.py +80 -5
  9. {qadence-1.8.0 → qadence-1.9.1}/qadence/backends/pyqtorch/config.py +10 -3
  10. {qadence-1.8.0 → qadence-1.9.1}/qadence/backends/pyqtorch/convert_ops.py +63 -2
  11. {qadence-1.8.0 → qadence-1.9.1}/qadence/blocks/primitive.py +1 -0
  12. {qadence-1.8.0 → qadence-1.9.1}/qadence/execution.py +0 -2
  13. {qadence-1.8.0 → qadence-1.9.1}/qadence/log_config.yaml +10 -0
  14. {qadence-1.8.0 → qadence-1.9.1}/qadence/measurements/shadow.py +97 -128
  15. {qadence-1.8.0 → qadence-1.9.1}/qadence/measurements/utils.py +2 -2
  16. {qadence-1.8.0 → qadence-1.9.1}/qadence/mitigations/readout.py +12 -6
  17. {qadence-1.8.0 → qadence-1.9.1}/qadence/ml_tools/__init__.py +4 -8
  18. qadence-1.9.1/qadence/ml_tools/callbacks/__init__.py +30 -0
  19. qadence-1.9.1/qadence/ml_tools/callbacks/callback.py +451 -0
  20. qadence-1.9.1/qadence/ml_tools/callbacks/callbackmanager.py +214 -0
  21. {qadence-1.8.0/qadence/ml_tools → qadence-1.9.1/qadence/ml_tools/callbacks}/saveload.py +11 -11
  22. qadence-1.9.1/qadence/ml_tools/callbacks/writer_registry.py +441 -0
  23. {qadence-1.8.0 → qadence-1.9.1}/qadence/ml_tools/config.py +132 -258
  24. {qadence-1.8.0 → qadence-1.9.1}/qadence/ml_tools/data.py +7 -3
  25. qadence-1.9.1/qadence/ml_tools/loss/__init__.py +10 -0
  26. qadence-1.9.1/qadence/ml_tools/loss/loss.py +87 -0
  27. qadence-1.9.1/qadence/ml_tools/optimize_step.py +89 -0
  28. qadence-1.9.1/qadence/ml_tools/stages.py +46 -0
  29. qadence-1.9.1/qadence/ml_tools/train_utils/__init__.py +7 -0
  30. qadence-1.9.1/qadence/ml_tools/train_utils/base_trainer.py +555 -0
  31. qadence-1.9.1/qadence/ml_tools/train_utils/config_manager.py +184 -0
  32. qadence-1.9.1/qadence/ml_tools/trainer.py +708 -0
  33. {qadence-1.8.0 → qadence-1.9.1}/qadence/model.py +1 -1
  34. qadence-1.9.1/qadence/noise/__init__.py +6 -0
  35. {qadence-1.8.0 → qadence-1.9.1}/qadence/noise/protocols.py +18 -53
  36. {qadence-1.8.0 → qadence-1.9.1}/qadence/operations/ham_evo.py +87 -26
  37. {qadence-1.8.0 → qadence-1.9.1}/qadence/transpile/noise.py +12 -5
  38. {qadence-1.8.0 → qadence-1.9.1}/qadence/types.py +15 -3
  39. qadence-1.8.0/qadence/ml_tools/optimize_step.py +0 -54
  40. qadence-1.8.0/qadence/ml_tools/printing.py +0 -154
  41. qadence-1.8.0/qadence/ml_tools/train_grad.py +0 -395
  42. qadence-1.8.0/qadence/ml_tools/train_no_grad.py +0 -199
  43. qadence-1.8.0/qadence/noise/__init__.py +0 -6
  44. qadence-1.8.0/qadence/noise/readout.py +0 -218
  45. {qadence-1.8.0 → qadence-1.9.1}/.coveragerc +0 -0
  46. {qadence-1.8.0 → qadence-1.9.1}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
  47. {qadence-1.8.0 → qadence-1.9.1}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  48. {qadence-1.8.0 → qadence-1.9.1}/.github/ISSUE_TEMPLATE/new-feature.yml +0 -0
  49. {qadence-1.8.0 → qadence-1.9.1}/.github/workflows/build_docs.yml +0 -0
  50. {qadence-1.8.0 → qadence-1.9.1}/.github/workflows/lint.yml +0 -0
  51. {qadence-1.8.0 → qadence-1.9.1}/.github/workflows/test_all.yml +0 -0
  52. {qadence-1.8.0 → qadence-1.9.1}/.github/workflows/test_examples.yml +0 -0
  53. {qadence-1.8.0 → qadence-1.9.1}/.github/workflows/test_fast.yml +0 -0
  54. {qadence-1.8.0 → qadence-1.9.1}/.gitignore +0 -0
  55. {qadence-1.8.0 → qadence-1.9.1}/.pre-commit-config.yaml +0 -0
  56. {qadence-1.8.0 → qadence-1.9.1}/LICENSE +0 -0
  57. {qadence-1.8.0 → qadence-1.9.1}/MANIFEST.in +0 -0
  58. {qadence-1.8.0 → qadence-1.9.1}/README.md +0 -0
  59. {qadence-1.8.0 → qadence-1.9.1}/qadence/analog/__init__.py +0 -0
  60. {qadence-1.8.0 → qadence-1.9.1}/qadence/analog/addressing.py +0 -0
  61. {qadence-1.8.0 → qadence-1.9.1}/qadence/analog/constants.py +0 -0
  62. {qadence-1.8.0 → qadence-1.9.1}/qadence/analog/device.py +0 -0
  63. {qadence-1.8.0 → qadence-1.9.1}/qadence/analog/hamiltonian_terms.py +0 -0
  64. {qadence-1.8.0 → qadence-1.9.1}/qadence/backend.py +0 -0
  65. {qadence-1.8.0 → qadence-1.9.1}/qadence/backends/__init__.py +0 -0
  66. {qadence-1.8.0 → qadence-1.9.1}/qadence/backends/api.py +0 -0
  67. {qadence-1.8.0 → qadence-1.9.1}/qadence/backends/horqrux/__init__.py +0 -0
  68. {qadence-1.8.0 → qadence-1.9.1}/qadence/backends/horqrux/backend.py +0 -0
  69. {qadence-1.8.0 → qadence-1.9.1}/qadence/backends/horqrux/config.py +0 -0
  70. {qadence-1.8.0 → qadence-1.9.1}/qadence/backends/horqrux/convert_ops.py +0 -0
  71. {qadence-1.8.0 → qadence-1.9.1}/qadence/backends/jax_utils.py +0 -0
  72. {qadence-1.8.0 → qadence-1.9.1}/qadence/backends/pulser/__init__.py +0 -0
  73. {qadence-1.8.0 → qadence-1.9.1}/qadence/backends/pulser/channels.py +0 -0
  74. {qadence-1.8.0 → qadence-1.9.1}/qadence/backends/pulser/cloud.py +0 -0
  75. {qadence-1.8.0 → qadence-1.9.1}/qadence/backends/pulser/config.py +0 -0
  76. {qadence-1.8.0 → qadence-1.9.1}/qadence/backends/pulser/convert_ops.py +0 -0
  77. {qadence-1.8.0 → qadence-1.9.1}/qadence/backends/pulser/devices.py +0 -0
  78. {qadence-1.8.0 → qadence-1.9.1}/qadence/backends/pulser/pulses.py +0 -0
  79. {qadence-1.8.0 → qadence-1.9.1}/qadence/backends/pulser/waveforms.py +0 -0
  80. {qadence-1.8.0 → qadence-1.9.1}/qadence/backends/pyqtorch/__init__.py +0 -0
  81. {qadence-1.8.0 → qadence-1.9.1}/qadence/backends/utils.py +0 -0
  82. {qadence-1.8.0 → qadence-1.9.1}/qadence/blocks/__init__.py +0 -0
  83. {qadence-1.8.0 → qadence-1.9.1}/qadence/blocks/abstract.py +0 -0
  84. {qadence-1.8.0 → qadence-1.9.1}/qadence/blocks/analog.py +0 -0
  85. {qadence-1.8.0 → qadence-1.9.1}/qadence/blocks/block_to_tensor.py +0 -0
  86. {qadence-1.8.0 → qadence-1.9.1}/qadence/blocks/composite.py +0 -0
  87. {qadence-1.8.0 → qadence-1.9.1}/qadence/blocks/embedding.py +0 -0
  88. {qadence-1.8.0 → qadence-1.9.1}/qadence/blocks/manipulate.py +0 -0
  89. {qadence-1.8.0 → qadence-1.9.1}/qadence/blocks/matrix.py +0 -0
  90. {qadence-1.8.0 → qadence-1.9.1}/qadence/blocks/utils.py +0 -0
  91. {qadence-1.8.0 → qadence-1.9.1}/qadence/circuit.py +0 -0
  92. {qadence-1.8.0 → qadence-1.9.1}/qadence/constructors/__init__.py +0 -0
  93. {qadence-1.8.0 → qadence-1.9.1}/qadence/constructors/ansatze.py +0 -0
  94. {qadence-1.8.0 → qadence-1.9.1}/qadence/constructors/daqc/__init__.py +0 -0
  95. {qadence-1.8.0 → qadence-1.9.1}/qadence/constructors/daqc/daqc.py +0 -0
  96. {qadence-1.8.0 → qadence-1.9.1}/qadence/constructors/daqc/gen_parser.py +0 -0
  97. {qadence-1.8.0 → qadence-1.9.1}/qadence/constructors/daqc/utils.py +0 -0
  98. {qadence-1.8.0 → qadence-1.9.1}/qadence/constructors/feature_maps.py +0 -0
  99. {qadence-1.8.0 → qadence-1.9.1}/qadence/constructors/hamiltonians.py +0 -0
  100. {qadence-1.8.0 → qadence-1.9.1}/qadence/constructors/iia.py +0 -0
  101. {qadence-1.8.0 → qadence-1.9.1}/qadence/constructors/qft.py +0 -0
  102. {qadence-1.8.0 → qadence-1.9.1}/qadence/constructors/rydberg_feature_maps.py +0 -0
  103. {qadence-1.8.0 → qadence-1.9.1}/qadence/constructors/rydberg_hea.py +0 -0
  104. {qadence-1.8.0 → qadence-1.9.1}/qadence/constructors/utils.py +0 -0
  105. {qadence-1.8.0 → qadence-1.9.1}/qadence/decompose.py +0 -0
  106. {qadence-1.8.0 → qadence-1.9.1}/qadence/divergences.py +0 -0
  107. {qadence-1.8.0 → qadence-1.9.1}/qadence/draw/__init__.py +0 -0
  108. {qadence-1.8.0 → qadence-1.9.1}/qadence/draw/assets/dark/measurement.png +0 -0
  109. {qadence-1.8.0 → qadence-1.9.1}/qadence/draw/assets/dark/measurement.svg +0 -0
  110. {qadence-1.8.0 → qadence-1.9.1}/qadence/draw/assets/light/measurement.png +0 -0
  111. {qadence-1.8.0 → qadence-1.9.1}/qadence/draw/assets/light/measurement.svg +0 -0
  112. {qadence-1.8.0 → qadence-1.9.1}/qadence/draw/themes.py +0 -0
  113. {qadence-1.8.0 → qadence-1.9.1}/qadence/draw/utils.py +0 -0
  114. {qadence-1.8.0 → qadence-1.9.1}/qadence/draw/vizbackend.py +0 -0
  115. {qadence-1.8.0 → qadence-1.9.1}/qadence/engines/__init__.py +0 -0
  116. {qadence-1.8.0 → qadence-1.9.1}/qadence/engines/differentiable_backend.py +0 -0
  117. {qadence-1.8.0 → qadence-1.9.1}/qadence/engines/jax/__init__.py +0 -0
  118. {qadence-1.8.0 → qadence-1.9.1}/qadence/engines/jax/differentiable_backend.py +0 -0
  119. {qadence-1.8.0 → qadence-1.9.1}/qadence/engines/jax/differentiable_expectation.py +0 -0
  120. {qadence-1.8.0 → qadence-1.9.1}/qadence/engines/torch/__init__.py +0 -0
  121. {qadence-1.8.0 → qadence-1.9.1}/qadence/engines/torch/differentiable_backend.py +0 -0
  122. {qadence-1.8.0 → qadence-1.9.1}/qadence/engines/torch/differentiable_expectation.py +0 -0
  123. {qadence-1.8.0 → qadence-1.9.1}/qadence/exceptions/__init__.py +0 -0
  124. {qadence-1.8.0 → qadence-1.9.1}/qadence/exceptions/exceptions.py +0 -0
  125. {qadence-1.8.0 → qadence-1.9.1}/qadence/extensions.py +0 -0
  126. {qadence-1.8.0 → qadence-1.9.1}/qadence/libs.py +0 -0
  127. {qadence-1.8.0 → qadence-1.9.1}/qadence/logger.py +0 -0
  128. {qadence-1.8.0 → qadence-1.9.1}/qadence/measurements/__init__.py +0 -0
  129. {qadence-1.8.0 → qadence-1.9.1}/qadence/measurements/protocols.py +0 -0
  130. {qadence-1.8.0 → qadence-1.9.1}/qadence/measurements/samples.py +0 -0
  131. {qadence-1.8.0 → qadence-1.9.1}/qadence/measurements/tomography.py +0 -0
  132. {qadence-1.8.0 → qadence-1.9.1}/qadence/mitigations/__init__.py +0 -0
  133. {qadence-1.8.0 → qadence-1.9.1}/qadence/mitigations/analog_zne.py +0 -0
  134. {qadence-1.8.0 → qadence-1.9.1}/qadence/mitigations/protocols.py +0 -0
  135. {qadence-1.8.0 → qadence-1.9.1}/qadence/ml_tools/constructors.py +0 -0
  136. {qadence-1.8.0 → qadence-1.9.1}/qadence/ml_tools/models.py +0 -0
  137. {qadence-1.8.0 → qadence-1.9.1}/qadence/ml_tools/parameters.py +0 -0
  138. {qadence-1.8.0 → qadence-1.9.1}/qadence/ml_tools/tensors.py +0 -0
  139. {qadence-1.8.0 → qadence-1.9.1}/qadence/ml_tools/utils.py +0 -0
  140. {qadence-1.8.0 → qadence-1.9.1}/qadence/operations/__init__.py +0 -0
  141. {qadence-1.8.0 → qadence-1.9.1}/qadence/operations/analog.py +0 -0
  142. {qadence-1.8.0 → qadence-1.9.1}/qadence/operations/control_ops.py +0 -0
  143. {qadence-1.8.0 → qadence-1.9.1}/qadence/operations/parametric.py +0 -0
  144. {qadence-1.8.0 → qadence-1.9.1}/qadence/operations/primitive.py +0 -0
  145. {qadence-1.8.0 → qadence-1.9.1}/qadence/overlap.py +0 -0
  146. {qadence-1.8.0 → qadence-1.9.1}/qadence/parameters.py +0 -0
  147. {qadence-1.8.0 → qadence-1.9.1}/qadence/protocols.py +0 -0
  148. {qadence-1.8.0 → qadence-1.9.1}/qadence/py.typed +0 -0
  149. {qadence-1.8.0 → qadence-1.9.1}/qadence/qubit_support.py +0 -0
  150. {qadence-1.8.0 → qadence-1.9.1}/qadence/register.py +0 -0
  151. {qadence-1.8.0 → qadence-1.9.1}/qadence/serial_expr_grammar.peg +0 -0
  152. {qadence-1.8.0 → qadence-1.9.1}/qadence/serialization.py +0 -0
  153. {qadence-1.8.0 → qadence-1.9.1}/qadence/states.py +0 -0
  154. {qadence-1.8.0 → qadence-1.9.1}/qadence/transpile/__init__.py +0 -0
  155. {qadence-1.8.0 → qadence-1.9.1}/qadence/transpile/apply_fn.py +0 -0
  156. {qadence-1.8.0 → qadence-1.9.1}/qadence/transpile/block.py +0 -0
  157. {qadence-1.8.0 → qadence-1.9.1}/qadence/transpile/circuit.py +0 -0
  158. {qadence-1.8.0 → qadence-1.9.1}/qadence/transpile/digitalize.py +0 -0
  159. {qadence-1.8.0 → qadence-1.9.1}/qadence/transpile/flatten.py +0 -0
  160. {qadence-1.8.0 → qadence-1.9.1}/qadence/transpile/invert.py +0 -0
  161. {qadence-1.8.0 → qadence-1.9.1}/qadence/transpile/transpile.py +0 -0
  162. {qadence-1.8.0 → qadence-1.9.1}/qadence/utils.py +0 -0
  163. {qadence-1.8.0 → qadence-1.9.1}/renovate.json +0 -0
  164. {qadence-1.8.0 → qadence-1.9.1}/setup.py +0 -0
@@ -1,10 +1,9 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: qadence
3
- Version: 1.8.0
3
+ Version: 1.9.1
4
4
  Summary: Pasqal interface for circuit-based quantum computing SDKs
5
5
  Author-email: Aleksander Wennersteen <aleksander.wennersteen@pasqal.com>, Gert-Jan Both <gert-jan.both@pasqal.com>, Niklas Heim <niklas.heim@pasqal.com>, Mario Dagrada <mario.dagrada@pasqal.com>, Vincent Elfving <vincent.elfving@pasqal.com>, Dominik Seitz <dominik.seitz@pasqal.com>, Roland Guichard <roland.guichard@pasqal.com>, "Joao P. Moutinho" <joao.moutinho@pasqal.com>, Vytautas Abramavicius <vytautas.abramavicius@pasqal.com>, Gergana Velikova <gergana.velikova@pasqal.com>, Eduardo Maschio <eduardo.maschio@pasqal.com>, Smit Chaudhary <smit.chaudhary@pasqal.com>, Ignacio Fernández Graña <ignacio.fernandez-grana@pasqal.com>, Charles Moussa <charles.moussa@pasqal.com>, Giorgio Tosti Balducci <giorgio.tosti-balducci@pasqal.com>, Daniele Cucurachi <daniele.cucurachi@pasqal.com>
6
6
  License: Apache 2.0
7
- License-File: LICENSE
8
7
  Classifier: License :: OSI Approved :: Apache Software License
9
8
  Classifier: Programming Language :: Python
10
9
  Classifier: Programming Language :: Python :: 3
@@ -22,7 +21,7 @@ Requires-Dist: matplotlib
22
21
  Requires-Dist: nevergrad
23
22
  Requires-Dist: numpy
24
23
  Requires-Dist: openfermion
25
- Requires-Dist: pyqtorch==1.5.1
24
+ Requires-Dist: pyqtorch==1.6.0
26
25
  Requires-Dist: pyyaml
27
26
  Requires-Dist: rich
28
27
  Requires-Dist: scipy
@@ -54,7 +53,7 @@ Requires-Dist: mlflow; extra == 'mlflow'
54
53
  Provides-Extra: protocols
55
54
  Requires-Dist: qadence-protocols; extra == 'protocols'
56
55
  Provides-Extra: pulser
57
- Requires-Dist: pasqal-cloud==0.12.4; extra == 'pulser'
56
+ Requires-Dist: pasqal-cloud==0.12.5; extra == 'pulser'
58
57
  Requires-Dist: pulser-core==1.1.1; extra == 'pulser'
59
58
  Requires-Dist: pulser-simulation==1.1.1; extra == 'pulser'
60
59
  Provides-Extra: visualization
@@ -16,11 +16,11 @@ nav:
16
16
  - Contents:
17
17
  - Block system: content/block_system.md
18
18
  - Parametric programs: content/parameters.md
19
- - Time-dependent generators: content/time_dependent.md
20
19
  - Quantum models: content/quantummodels.md
21
20
  - Quantum registers: content/register.md
22
21
  - State initialization: content/state_init.md
23
22
  - Arbitrary Hamiltonians: content/hamiltonians.md
23
+ - Time-dependent generators: content/time_dependent.md
24
24
  - QML Constructors: content/qml_constructors.md
25
25
  - Wavefunction overlaps: content/overlap.md
26
26
  - Backends: content/backends.md
@@ -42,7 +42,9 @@ nav:
42
42
 
43
43
  - Variational quantum algorithms:
44
44
  - tutorials/qml/index.md
45
- - Training tools: tutorials/qml/ml_tools.md
45
+ - Training: tutorials/qml/ml_tools/trainer.md
46
+ - Training Callbacks: tutorials/qml/ml_tools/callbacks.md
47
+ - Data and Configurations: tutorials/qml/ml_tools/data_and_config.md
46
48
  - Configuring a QNN: tutorials/qml/config_qnn.md
47
49
  - Quantum circuit learning: tutorials/qml/qcl.md
48
50
  - Solving MaxCut with QAOA: tutorials/qml/qaoa.md
@@ -26,7 +26,7 @@ authors = [
26
26
  ]
27
27
  requires-python = ">=3.9"
28
28
  license = { text = "Apache 2.0" }
29
- version = "1.8.0"
29
+ version = "1.9.1"
30
30
  classifiers = [
31
31
  "License :: OSI Approved :: Apache Software License",
32
32
  "Programming Language :: Python",
@@ -50,7 +50,7 @@ dependencies = [
50
50
  "jsonschema",
51
51
  "nevergrad",
52
52
  "scipy",
53
- "pyqtorch==1.5.1",
53
+ "pyqtorch==1.6.0",
54
54
  "pyyaml",
55
55
  "matplotlib",
56
56
  "Arpeggio==2.0.2",
@@ -64,7 +64,7 @@ allow-ambiguous-features = true
64
64
  pulser = [
65
65
  "pulser-core==1.1.1",
66
66
  "pulser-simulation==1.1.1",
67
- "pasqal-cloud==0.12.4",
67
+ "pasqal-cloud==0.12.5",
68
68
  ]
69
69
  visualization = [
70
70
  "graphviz",
@@ -34,7 +34,7 @@ logger.setLevel(LOG_LEVEL)
34
34
  [
35
35
  h.setLevel(LOG_LEVEL) # type: ignore[func-returns-value]
36
36
  for h in logger.handlers
37
- if h.get_name() == "console"
37
+ if h.get_name() == "console" or h.get_name() == "richconsole"
38
38
  ]
39
39
  logger.debug(f"Qadence logger successfully setup with log level {LOG_LEVEL}")
40
40
 
@@ -78,9 +78,8 @@ def _build_rot_ham_evo(
78
78
  if block.add_pattern and h_addr is not None:
79
79
  h_block += h_addr
80
80
  duration = block.parameters.duration
81
- h_norm = block.parameters.h_norm
82
81
  h_block += h_drive
83
- return HamEvo(h_block / h_norm, duration * h_norm / 1000)
82
+ return HamEvo(h_block, duration / 1000)
84
83
 
85
84
 
86
85
  def _analog_to_hevo(
@@ -36,7 +36,11 @@ def general_psr(spectrum: Tensor, n_eqs: int | None = None, shift_prefac: float
36
36
  sorted_unique_spectral_gaps = torch.tensor(list(sorted_unique_spectral_gaps)[:n_eqs])
37
37
 
38
38
  if n_eqs == 1:
39
- return single_gap_psr
39
+ return partial(
40
+ single_gap_psr,
41
+ spectral_gap=sorted_unique_spectral_gaps,
42
+ shift=shift_prefac * torch.tensor([PI / 2], dtype=torch.get_default_dtype()),
43
+ )
40
44
  else:
41
45
  return partial(
42
46
  multi_gap_psr,
@@ -110,7 +114,9 @@ def multi_gap_psr(
110
114
  batch_size = max(t.size(0) for t in param_dict.values())
111
115
 
112
116
  # get shift values
113
- shifts = shift_prefac * torch.linspace(PI / 2 - PI / 5, PI / 2 + PI / 5, n_eqs)
117
+ shifts = shift_prefac * torch.linspace(
118
+ PI / 2 - PI / 4, PI / 2 + PI / 5, n_eqs
119
+ ) # breaking the symmetry of sampling range around PI/2
114
120
  device = torch.device("cpu")
115
121
  try:
116
122
  device = [v.device for v in param_dict.values()][0]
@@ -25,7 +25,6 @@ from qadence.measurements import Measurements
25
25
  from qadence.mitigations import Mitigations
26
26
  from qadence.mitigations.protocols import apply_mitigation
27
27
  from qadence.noise import NoiseHandler
28
- from qadence.noise.protocols import apply_readout_noise
29
28
  from qadence.overlap import overlap_exact
30
29
  from qadence.register import Register
31
30
  from qadence.transpile import transpile
@@ -311,8 +310,6 @@ class Backend(BackendInterface):
311
310
  from qadence.transpile import invert_endianness
312
311
 
313
312
  samples = invert_endianness(samples)
314
- if noise is not None:
315
- samples = apply_readout_noise(noise=noise, samples=samples)
316
313
  if mitigation is not None:
317
314
  logger.warning(
318
315
  "Mitigation protocol is deprecated. Use qadence-protocols instead.",
@@ -354,27 +351,14 @@ class Backend(BackendInterface):
354
351
  noise=noise,
355
352
  )
356
353
  support = sorted(list(circuit.abstract.register.support))
357
- # TODO: There should be a better check for batched density matrices.
358
- if dms.size()[0] > 1:
359
- res_list = [
360
- [
361
- obs.native(
362
- dm.squeeze(), param_values, qubit_support=support, noise=noise
363
- )
364
- for dm in dms
365
- ]
366
- for obs in observable
367
- ]
368
- res = torch.stack(
369
- [torch.transpose(torch.stack(res), 0, -1) for res in res_list]
370
- )
371
-
372
- else:
373
- res_list = [
374
- obs.native(dms, param_values, qubit_support=support) for obs in observable
354
+ res_list = [
355
+ [
356
+ obs.native(dm.squeeze(), param_values, qubit_support=support, noise=noise)
357
+ for dm in dms
375
358
  ]
376
- res = torch.transpose(torch.stack(res_list), 0, 1)
377
- res = res if len(res.shape) > 0 else res.reshape(1)
359
+ for obs in observable
360
+ ]
361
+ res = torch.stack([torch.transpose(torch.stack(res), 0, -1) for res in res_list])
378
362
  return res.real
379
363
  elif mitigation is not None:
380
364
  logger.warning(
@@ -23,22 +23,62 @@ from qadence.circuit import QuantumCircuit
23
23
  from qadence.measurements import Measurements
24
24
  from qadence.mitigations.protocols import Mitigations, apply_mitigation
25
25
  from qadence.noise import NoiseHandler
26
- from qadence.noise.protocols import apply_readout_noise
27
26
  from qadence.transpile import (
28
27
  chain_single_qubit_ops,
29
28
  flatten,
30
29
  invert_endianness,
31
30
  scale_primitive_blocks_only,
31
+ set_noise,
32
32
  transpile,
33
33
  )
34
34
  from qadence.types import BackendName, Endianness, Engine
35
35
 
36
36
  from .config import Configuration, default_passes
37
- from .convert_ops import convert_block
37
+ from .convert_ops import convert_block, convert_readout_noise
38
38
 
39
39
  logger = getLogger(__name__)
40
40
 
41
41
 
42
+ def set_noise_abstract_to_native(circuit: ConvertedCircuit, config: Configuration) -> None:
43
+ """Set noise in native blocks from the abstract ones with noise.
44
+
45
+ Args:
46
+ circuit (ConvertedCircuit): Input converted circuit.
47
+ """
48
+ ops = convert_block(circuit.abstract.block, n_qubits=circuit.native.n_qubits, config=config)
49
+ circuit.native = pyq.QuantumCircuit(circuit.native.n_qubits, ops, circuit.native.readout_noise)
50
+
51
+
52
+ def set_readout_noise(circuit: ConvertedCircuit, noise: NoiseHandler) -> None:
53
+ """Set readout noise in place in native.
54
+
55
+ Args:
56
+ circuit (ConvertedCircuit): Input converted circuit.
57
+ noise (NoiseHandler | None): Noise.
58
+ """
59
+ readout = convert_readout_noise(circuit.abstract.n_qubits, noise)
60
+ if readout:
61
+ circuit.native.readout_noise = readout
62
+
63
+
64
+ def set_block_and_readout_noises(
65
+ circuit: ConvertedCircuit, noise: NoiseHandler | None, config: Configuration
66
+ ) -> None:
67
+ """Add noise on blocks and readout on circuit.
68
+
69
+ We first start by adding noise to the abstract blocks. Then we do a conversion to their
70
+ native representation. Finally, we add readout.
71
+
72
+ Args:
73
+ circuit (ConvertedCircuit): Input circuit.
74
+ noise (NoiseHandler | None): Noise to add.
75
+ """
76
+ if noise:
77
+ set_noise(circuit, noise)
78
+ set_noise_abstract_to_native(circuit, config)
79
+ set_readout_noise(circuit, noise)
80
+
81
+
42
82
  @dataclass(frozen=True, eq=True)
43
83
  class Backend(BackendInterface):
44
84
  """PyQTorch backend."""
@@ -56,6 +96,17 @@ class Backend(BackendInterface):
56
96
  logger.debug("Initialised")
57
97
 
58
98
  def circuit(self, circuit: QuantumCircuit) -> ConvertedCircuit:
99
+ """Return the converted circuit.
100
+
101
+ Note that to get a representation with noise, noise
102
+ should be passed within the config.
103
+
104
+ Args:
105
+ circuit (QuantumCircuit): Original circuit
106
+
107
+ Returns:
108
+ ConvertedCircuit: ConvertedCircuit instance for backend.
109
+ """
59
110
  passes = self.config.transpilation_passes
60
111
  if passes is None:
61
112
  passes = default_passes(self.config)
@@ -63,9 +114,30 @@ class Backend(BackendInterface):
63
114
  original_circ = circuit
64
115
  if len(passes) > 0:
65
116
  circuit = transpile(*passes)(circuit)
117
+ # Setting noise in the circuit.
118
+ if self.config.noise:
119
+ set_noise(circuit, self.config.noise)
66
120
 
67
121
  ops = convert_block(circuit.block, n_qubits=circuit.n_qubits, config=self.config)
68
- native = pyq.QuantumCircuit(circuit.n_qubits, ops)
122
+ readout_noise = (
123
+ convert_readout_noise(circuit.n_qubits, self.config.noise)
124
+ if self.config.noise
125
+ else None
126
+ )
127
+ if self.config.dropout_probability == 0:
128
+ native = pyq.QuantumCircuit(
129
+ circuit.n_qubits,
130
+ ops,
131
+ readout_noise,
132
+ )
133
+ else:
134
+ native = pyq.DropoutQuantumCircuit(
135
+ circuit.n_qubits,
136
+ ops,
137
+ readout_noise,
138
+ dropout_prob=self.config.dropout_probability,
139
+ dropout_mode=self.config.dropout_mode,
140
+ )
69
141
  return ConvertedCircuit(native=native, abstract=circuit, original=original_circ)
70
142
 
71
143
  def observable(self, observable: AbstractBlock, n_qubits: int) -> ConvertedObservable:
@@ -116,6 +188,7 @@ class Backend(BackendInterface):
116
188
  noise: NoiseHandler | None = None,
117
189
  endianness: Endianness = Endianness.BIG,
118
190
  ) -> Tensor:
191
+ set_block_and_readout_noises(circuit, noise, self.config)
119
192
  state = self.run(
120
193
  circuit,
121
194
  param_values=param_values,
@@ -152,6 +225,9 @@ class Backend(BackendInterface):
152
225
  "Looping expectation does not make sense with batched initial state. "
153
226
  "Define your initial state with `batch_size=1`"
154
227
  )
228
+
229
+ set_block_and_readout_noises(circuit, noise, self.config)
230
+
155
231
  list_expvals = []
156
232
  observables = observable if isinstance(observable, list) else [observable]
157
233
  for vals in to_list_of_dicts(param_values):
@@ -206,12 +282,11 @@ class Backend(BackendInterface):
206
282
  elif state is not None and pyqify_state:
207
283
  n_qubits = circuit.abstract.n_qubits
208
284
  state = pyqify(state, n_qubits) if pyqify_state else state
285
+ set_block_and_readout_noises(circuit, noise, self.config)
209
286
  samples: list[Counter] = circuit.native.sample(
210
287
  state=state, values=param_values, n_shots=n_shots
211
288
  )
212
289
  samples = invert_endianness(samples) if endianness != Endianness.BIG else samples
213
- if noise is not None:
214
- samples = apply_readout_noise(noise=noise, samples=samples)
215
290
  if mitigation is not None:
216
291
  logger.warning(
217
292
  "Mitigation protocol is deprecated. Use qadence-protocols instead.",
@@ -4,17 +4,16 @@ from dataclasses import dataclass
4
4
  from logging import getLogger
5
5
  from typing import Callable
6
6
 
7
- from pyqtorch.utils import SolverType
8
-
9
7
  from qadence.analog import add_background_hamiltonian
10
8
  from qadence.backend import BackendConfiguration
9
+ from qadence.noise import NoiseHandler
11
10
  from qadence.transpile import (
12
11
  blockfn_to_circfn,
13
12
  chain_single_qubit_ops,
14
13
  flatten,
15
14
  scale_primitive_blocks_only,
16
15
  )
17
- from qadence.types import AlgoHEvo
16
+ from qadence.types import AlgoHEvo, DropoutMode, SolverType
18
17
 
19
18
  logger = getLogger(__name__)
20
19
 
@@ -63,3 +62,11 @@ class Configuration(BackendConfiguration):
63
62
 
64
63
  Loop over the batch of parameters to only allocate a single wavefunction at any given time.
65
64
  """
65
+
66
+ noise: NoiseHandler | None = None
67
+ """NoiseHandler containing readout noise applied in backend."""
68
+
69
+ dropout_probability: float = 0.0
70
+ """Quantum dropout probability (0 means no dropout)."""
71
+ dropout_mode: DropoutMode = DropoutMode.ROTATIONAL
72
+ """Type of quantum dropout to perform."""
@@ -175,8 +175,23 @@ def sympy_to_pyq(expr: sympy.Expr) -> ConcretizedCallable | Tensor:
175
175
 
176
176
 
177
177
  def convert_block(
178
- block: AbstractBlock, n_qubits: int = None, config: Configuration = None
178
+ block: AbstractBlock,
179
+ n_qubits: int = None,
180
+ config: Configuration = None,
179
181
  ) -> Sequence[Module | Tensor | str | sympy.Expr]:
182
+ """Convert block to native Pyqtorch representation.
183
+
184
+ Args:
185
+ block (AbstractBlock): Block to convert.
186
+ n_qubits (int, optional): Number of qubits. Defaults to None.
187
+ config (Configuration, optional): Backend configuration instance. Defaults to None.
188
+
189
+ Raises:
190
+ NotImplementedError: For non supported blocks.
191
+
192
+ Returns:
193
+ Sequence[Module | Tensor | str | sympy.Expr]: List of native operations.
194
+ """
180
195
  if isinstance(block, (Tensor, str, sympy.Expr)): # case for hamevo generators
181
196
  if isinstance(block, Tensor):
182
197
  block = block.permute(1, 2, 0) # put batch size in the back
@@ -208,8 +223,10 @@ def convert_block(
208
223
  return [pyq.Scale(pyq.Sequence(scaled_ops), param)]
209
224
 
210
225
  elif isinstance(block, TimeEvolutionBlock):
226
+ duration = block.duration # type: ignore [attr-defined]
211
227
  if getattr(block.generator, "is_time_dependent", False):
212
228
  config._use_gate_params = False
229
+ duration = config.get_param_name(block)[1]
213
230
  generator = convert_block(block.generator, config=config)[0] # type: ignore [arg-type]
214
231
  elif isinstance(block.generator, sympy.Basic):
215
232
  generator = config.get_param_name(block)[1]
@@ -228,12 +245,26 @@ def convert_block(
228
245
  generator = convert_block(block.generator, n_qubits, config)[0] # type: ignore[arg-type]
229
246
  time_param = config.get_param_name(block)[0]
230
247
 
248
+ # convert noise operators here
249
+ noise_operators: list = [
250
+ convert_block(noise_block, config=config)[0] for noise_block in block.noise_operators
251
+ ]
252
+ if len(noise_operators) > 0:
253
+ # squeeze batch size for noise operators
254
+ noise_operators = [
255
+ pyq_op.tensor(full_support=qubit_support).squeeze(-1) for pyq_op in noise_operators
256
+ ]
257
+
231
258
  return [
232
259
  pyq.HamiltonianEvolution(
233
260
  qubit_support=qubit_support,
234
261
  generator=generator,
235
262
  time=time_param,
236
263
  cache_length=0,
264
+ duration=duration,
265
+ solver=config.ode_solver,
266
+ steps=config.n_steps_hevo,
267
+ noise_operators=noise_operators,
237
268
  )
238
269
  ]
239
270
 
@@ -320,7 +351,16 @@ def convert_block(
320
351
  )
321
352
 
322
353
 
323
- def convert_digital_noise(noise: NoiseHandler) -> pyq.noise.NoiseProtocol:
354
+ def convert_digital_noise(noise: NoiseHandler) -> pyq.noise.NoiseProtocol | None:
355
+ """Convert the digital noise into pyqtorch NoiseProtocol.
356
+
357
+ Args:
358
+ noise (NoiseHandler): Noise to convert.
359
+
360
+ Returns:
361
+ pyq.noise.NoiseProtocol | None: Pyqtorch native noise protocol
362
+ if there are any digital noise protocols.
363
+ """
324
364
  digital_part = noise.filter(NoiseProtocol.DIGITAL)
325
365
  if digital_part is None:
326
366
  return None
@@ -330,3 +370,24 @@ def convert_digital_noise(noise: NoiseHandler) -> pyq.noise.NoiseProtocol:
330
370
  for proto, option in zip(digital_part.protocol, digital_part.options)
331
371
  ]
332
372
  )
373
+
374
+
375
+ def convert_readout_noise(n_qubits: int, noise: NoiseHandler) -> pyq.noise.ReadoutNoise | None:
376
+ """Convert the readout noise into pyqtorch ReadoutNoise.
377
+
378
+ Args:
379
+ n_qubits (int): Number of qubits
380
+ noise (NoiseHandler): Noise to convert.
381
+
382
+ Returns:
383
+ pyq.noise.ReadoutNoise | None: Pyqtorch native ReadoutNoise instance
384
+ if readout is is noise.
385
+ """
386
+ readout_part = noise.filter(NoiseProtocol.READOUT)
387
+ if readout_part is None:
388
+ return None
389
+
390
+ if readout_part.protocol[0] == NoiseProtocol.READOUT.INDEPENDENT:
391
+ return pyq.noise.ReadoutNoise(n_qubits, **readout_part.options[0])
392
+ else:
393
+ return pyq.noise.CorrelatedReadoutNoise(**readout_part.options[0])
@@ -356,6 +356,7 @@ class TimeEvolutionBlock(ParametricBlock):
356
356
  """
357
357
 
358
358
  name = "TimeEvolutionBlock"
359
+ noise_operators: list = list()
359
360
 
360
361
  @property
361
362
  def has_parametrized_generator(self) -> bool:
@@ -248,8 +248,6 @@ def _(
248
248
  configuration: Union[BackendConfiguration, dict, None] = None,
249
249
  ) -> Tensor:
250
250
  observable = observable if isinstance(observable, list) else [observable]
251
- if backend == BackendName.PYQTORCH:
252
- diff_mode = DiffMode.AD
253
251
  bknd = backend_factory(backend, diff_mode=diff_mode, configuration=configuration)
254
252
  conv = bknd.convert(circuit, observable)
255
253
 
@@ -4,11 +4,17 @@ formatters:
4
4
  base:
5
5
  format: "%(levelname) -5s %(asctime)s - %(name)s: %(message)s"
6
6
  datefmt: "%Y-%m-%d %H:%M:%S"
7
+ empty:
8
+ format: "%(message)s" # Rich formatter for cleaner output
9
+ datefmt: "%Y-%m-%d %H:%M:%S"
7
10
  handlers:
8
11
  console:
9
12
  class: logging.StreamHandler
10
13
  formatter: base
11
14
  stream: ext://sys.stderr
15
+ richconsole:
16
+ class: rich.logging.RichHandler
17
+ formatter: empty
12
18
  loggers:
13
19
  qadence:
14
20
  level: INFO
@@ -22,3 +28,7 @@ loggers:
22
28
  level: INFO
23
29
  handlers: [console]
24
30
  propagate: yes
31
+ ml_tools:
32
+ level: INFO
33
+ handlers: [richconsole]
34
+ propagate: false