qoro-divi 0.2.0b1__py3-none-any.whl → 0.6.0__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.
Files changed (92) hide show
  1. divi/__init__.py +1 -2
  2. divi/backends/__init__.py +10 -0
  3. divi/backends/_backend_properties_conversion.py +227 -0
  4. divi/backends/_circuit_runner.py +70 -0
  5. divi/backends/_execution_result.py +70 -0
  6. divi/backends/_parallel_simulator.py +486 -0
  7. divi/backends/_qoro_service.py +663 -0
  8. divi/backends/_qpu_system.py +101 -0
  9. divi/backends/_results_processing.py +133 -0
  10. divi/circuits/__init__.py +13 -0
  11. divi/{exp/cirq → circuits/_cirq}/__init__.py +1 -2
  12. divi/circuits/_cirq/_parser.py +110 -0
  13. divi/circuits/_cirq/_qasm_export.py +78 -0
  14. divi/circuits/_core.py +391 -0
  15. divi/{qasm.py → circuits/_qasm_conversion.py} +73 -14
  16. divi/circuits/_qasm_validation.py +694 -0
  17. divi/qprog/__init__.py +27 -8
  18. divi/qprog/_expectation.py +181 -0
  19. divi/qprog/_hamiltonians.py +281 -0
  20. divi/qprog/algorithms/__init__.py +16 -0
  21. divi/qprog/algorithms/_ansatze.py +368 -0
  22. divi/qprog/algorithms/_custom_vqa.py +263 -0
  23. divi/qprog/algorithms/_pce.py +262 -0
  24. divi/qprog/algorithms/_qaoa.py +579 -0
  25. divi/qprog/algorithms/_vqe.py +262 -0
  26. divi/qprog/batch.py +387 -74
  27. divi/qprog/checkpointing.py +556 -0
  28. divi/qprog/exceptions.py +9 -0
  29. divi/qprog/optimizers.py +1014 -43
  30. divi/qprog/quantum_program.py +243 -412
  31. divi/qprog/typing.py +62 -0
  32. divi/qprog/variational_quantum_algorithm.py +1208 -0
  33. divi/qprog/workflows/__init__.py +10 -0
  34. divi/qprog/{_graph_partitioning.py → workflows/_graph_partitioning.py} +139 -95
  35. divi/qprog/workflows/_qubo_partitioning.py +221 -0
  36. divi/qprog/workflows/_vqe_sweep.py +560 -0
  37. divi/reporting/__init__.py +7 -0
  38. divi/reporting/_pbar.py +127 -0
  39. divi/reporting/_qlogger.py +68 -0
  40. divi/reporting/_reporter.py +155 -0
  41. {qoro_divi-0.2.0b1.dist-info → qoro_divi-0.6.0.dist-info}/METADATA +43 -15
  42. qoro_divi-0.6.0.dist-info/RECORD +47 -0
  43. {qoro_divi-0.2.0b1.dist-info → qoro_divi-0.6.0.dist-info}/WHEEL +1 -1
  44. qoro_divi-0.6.0.dist-info/licenses/LICENSES/.license-header +3 -0
  45. divi/_pbar.py +0 -73
  46. divi/circuits.py +0 -139
  47. divi/exp/cirq/_lexer.py +0 -126
  48. divi/exp/cirq/_parser.py +0 -889
  49. divi/exp/cirq/_qasm_export.py +0 -37
  50. divi/exp/cirq/_qasm_import.py +0 -35
  51. divi/exp/cirq/exception.py +0 -21
  52. divi/exp/scipy/_cobyla.py +0 -342
  53. divi/exp/scipy/pyprima/LICENCE.txt +0 -28
  54. divi/exp/scipy/pyprima/__init__.py +0 -263
  55. divi/exp/scipy/pyprima/cobyla/__init__.py +0 -0
  56. divi/exp/scipy/pyprima/cobyla/cobyla.py +0 -599
  57. divi/exp/scipy/pyprima/cobyla/cobylb.py +0 -849
  58. divi/exp/scipy/pyprima/cobyla/geometry.py +0 -240
  59. divi/exp/scipy/pyprima/cobyla/initialize.py +0 -269
  60. divi/exp/scipy/pyprima/cobyla/trustregion.py +0 -540
  61. divi/exp/scipy/pyprima/cobyla/update.py +0 -331
  62. divi/exp/scipy/pyprima/common/__init__.py +0 -0
  63. divi/exp/scipy/pyprima/common/_bounds.py +0 -41
  64. divi/exp/scipy/pyprima/common/_linear_constraints.py +0 -46
  65. divi/exp/scipy/pyprima/common/_nonlinear_constraints.py +0 -64
  66. divi/exp/scipy/pyprima/common/_project.py +0 -224
  67. divi/exp/scipy/pyprima/common/checkbreak.py +0 -107
  68. divi/exp/scipy/pyprima/common/consts.py +0 -48
  69. divi/exp/scipy/pyprima/common/evaluate.py +0 -101
  70. divi/exp/scipy/pyprima/common/history.py +0 -39
  71. divi/exp/scipy/pyprima/common/infos.py +0 -30
  72. divi/exp/scipy/pyprima/common/linalg.py +0 -452
  73. divi/exp/scipy/pyprima/common/message.py +0 -336
  74. divi/exp/scipy/pyprima/common/powalg.py +0 -131
  75. divi/exp/scipy/pyprima/common/preproc.py +0 -393
  76. divi/exp/scipy/pyprima/common/present.py +0 -5
  77. divi/exp/scipy/pyprima/common/ratio.py +0 -56
  78. divi/exp/scipy/pyprima/common/redrho.py +0 -49
  79. divi/exp/scipy/pyprima/common/selectx.py +0 -346
  80. divi/interfaces.py +0 -25
  81. divi/parallel_simulator.py +0 -258
  82. divi/qlogger.py +0 -119
  83. divi/qoro_service.py +0 -343
  84. divi/qprog/_mlae.py +0 -182
  85. divi/qprog/_qaoa.py +0 -440
  86. divi/qprog/_vqe.py +0 -275
  87. divi/qprog/_vqe_sweep.py +0 -144
  88. divi/utils.py +0 -116
  89. qoro_divi-0.2.0b1.dist-info/RECORD +0 -58
  90. /divi/{qem.py → circuits/qem.py} +0 -0
  91. {qoro_divi-0.2.0b1.dist-info → qoro_divi-0.6.0.dist-info/licenses}/LICENSE +0 -0
  92. {qoro_divi-0.2.0b1.dist-info → qoro_divi-0.6.0.dist-info/licenses}/LICENSES/Apache-2.0.txt +0 -0
@@ -0,0 +1,68 @@
1
+ # SPDX-FileCopyrightText: 2025 Qoro Quantum Ltd <divi@qoroquantum.de>
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ import logging
6
+
7
+ from rich.logging import RichHandler
8
+
9
+
10
+ class CustomRichFormatter(logging.Formatter):
11
+ """
12
+ A custom log formatter that removes '._reporter' from the logger name.
13
+ Works with RichHandler.
14
+ """
15
+
16
+ def format(self, record):
17
+ # Modify the record's name attribute in place
18
+ if record.name.endswith("._reporter"):
19
+ record.name = record.name.removesuffix("._reporter")
20
+ return super().format(record)
21
+
22
+
23
+ def enable_logging(level=logging.INFO):
24
+ """
25
+ Enable logging for the divi package with Rich formatting.
26
+
27
+ Sets up a RichHandler that provides colorized, formatted log output
28
+ and removes the '._reporter' suffix from logger names.
29
+
30
+ Args:
31
+ level (int, optional): Logging level to set (e.g., logging.INFO,
32
+ logging.DEBUG). Defaults to logging.INFO.
33
+
34
+ Note:
35
+ This function clears any existing handlers and sets up a new handler
36
+ with custom formatting.
37
+ """
38
+ root_logger = logging.getLogger(__name__.split(".")[0])
39
+
40
+ handler = RichHandler(
41
+ rich_tracebacks=True,
42
+ show_time=True,
43
+ show_path=False,
44
+ markup=True,
45
+ )
46
+
47
+ # Use a simpler formatter since RichHandler handles time display
48
+ formatter = CustomRichFormatter(
49
+ "%(name)s - %(levelname)s - %(message)s",
50
+ )
51
+ handler.setFormatter(formatter)
52
+
53
+ root_logger.setLevel(level)
54
+ root_logger.handlers.clear()
55
+ root_logger.addHandler(handler)
56
+
57
+
58
+ def disable_logging():
59
+ """
60
+ Disable all logging for the divi package.
61
+
62
+ Removes all handlers and sets the logging level to above CRITICAL,
63
+ effectively suppressing all log messages. This is useful when using
64
+ progress bars that provide visual feedback.
65
+ """
66
+ root_logger = logging.getLogger(__name__.split(".")[0])
67
+ root_logger.handlers.clear()
68
+ root_logger.setLevel(logging.CRITICAL + 1)
@@ -0,0 +1,155 @@
1
+ # SPDX-FileCopyrightText: 2025-2026 Qoro Quantum Ltd <divi@qoroquantum.de>
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ import atexit
6
+ import logging
7
+ import os
8
+ from abc import ABC, abstractmethod
9
+ from queue import Queue
10
+
11
+ from rich.console import Console
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+
16
+ class ProgressReporter(ABC):
17
+ """An abstract base class for reporting progress of a quantum program."""
18
+
19
+ @abstractmethod
20
+ def update(self, **kwargs) -> None:
21
+ """Provides a progress update."""
22
+ pass
23
+
24
+ @abstractmethod
25
+ def info(self, message: str, **kwargs) -> None:
26
+ """Provides a simple informational message.
27
+
28
+ Args:
29
+ message: The message to display.
30
+ **kwargs: Additional keyword arguments for subclasses.
31
+ """
32
+ pass
33
+
34
+
35
+ class QueueProgressReporter(ProgressReporter):
36
+ """Reports progress by putting structured dictionaries onto a Queue."""
37
+
38
+ def __init__(self, job_id: str, progress_queue: Queue):
39
+ self._job_id = job_id
40
+ self._queue = progress_queue
41
+
42
+ def update(self, **kwargs):
43
+ payload = {"job_id": self._job_id, "progress": 1}
44
+ self._queue.put(payload)
45
+
46
+ def info(self, message: str, **kwargs):
47
+ payload = {"job_id": self._job_id, "progress": 0, "message": message}
48
+
49
+ if "Finished successfully!" in message:
50
+ payload["final_status"] = "Success"
51
+
52
+ if "poll_attempt" in kwargs:
53
+ # For polling, remove the message key so the last message persists.
54
+ del payload["message"]
55
+ payload["poll_attempt"] = kwargs["poll_attempt"]
56
+ payload["max_retries"] = kwargs["max_retries"]
57
+ payload["service_job_id"] = kwargs["service_job_id"]
58
+ payload["job_status"] = kwargs["job_status"]
59
+ else:
60
+ # For any other message, explicitly reset the polling attempt counter.
61
+ payload["poll_attempt"] = 0
62
+
63
+ self._queue.put(payload)
64
+
65
+
66
+ class LoggingProgressReporter(ProgressReporter):
67
+ """Reports progress by logging messages to the console."""
68
+
69
+ _atexit_registered = False
70
+
71
+ def __init__(self):
72
+ # Use the same console instance that RichHandler uses to avoid interference
73
+ self._console = Console(file=None) # file=None uses stdout, same as RichHandler
74
+ self._status = None # Track active status for overwriting messages
75
+ self._current_msg = None # Track current main message
76
+ self._polling_msg = None # Track current polling message
77
+ self._disable_progress = self._should_disable_progress()
78
+
79
+ def _ensure_atexit_hook(self):
80
+ if self._disable_progress or LoggingProgressReporter._atexit_registered:
81
+ return
82
+ atexit.register(self._close_status)
83
+ LoggingProgressReporter._atexit_registered = True
84
+
85
+ @staticmethod
86
+ def _should_disable_progress() -> bool:
87
+ disable_env = os.getenv("DIVI_DISABLE_PROGRESS", "").strip().lower()
88
+ return disable_env in {"1", "true", "yes", "on"}
89
+
90
+ def _close_status(self):
91
+ """Close any active status."""
92
+ if self._status:
93
+ self._status.__exit__(None, None, None)
94
+ self._status = None
95
+ self._current_msg = None
96
+ self._polling_msg = None
97
+
98
+ def _build_status_msg(self) -> str:
99
+ """Build combined status message from current message and polling info."""
100
+ parts = []
101
+ if self._current_msg:
102
+ parts.append(self._current_msg)
103
+ if self._polling_msg:
104
+ parts.append(self._polling_msg)
105
+ return " - ".join(parts) if parts else ""
106
+
107
+ def _update_or_create_status(self):
108
+ """Update existing status or create a new one with combined message."""
109
+ if self._disable_progress:
110
+ return
111
+ status_msg = self._build_status_msg()
112
+ if not status_msg:
113
+ return
114
+ self._ensure_atexit_hook()
115
+ if self._status:
116
+ self._status.update(status_msg)
117
+ else:
118
+ self._status = self._console.status(status_msg, spinner="aesthetic")
119
+ self._status.__enter__()
120
+
121
+ def update(self, **kwargs):
122
+ # Close any active status before logging
123
+ self._close_status()
124
+ logger.info(f"Finished Iteration #{kwargs['iteration']}")
125
+
126
+ def info(self, message: str, overwrite: bool = False, **kwargs):
127
+ if self._disable_progress:
128
+ logger.info(message)
129
+ return
130
+ # A special check for iteration updates to use Rich's status for overwriting
131
+ if "poll_attempt" in kwargs:
132
+ self._polling_msg = (
133
+ f"Job [cyan]{kwargs['service_job_id'].split('-')[0]}[/cyan] is "
134
+ f"{kwargs['job_status']}. Polling attempt {kwargs['poll_attempt']} / "
135
+ f"{kwargs['max_retries']}"
136
+ )
137
+ self._update_or_create_status()
138
+ return
139
+
140
+ # Use Rich's status for iteration messages to enable overwriting
141
+ if "iteration" in kwargs:
142
+ self._current_msg = f"Iteration #{kwargs['iteration'] + 1}: {message}"
143
+ self._update_or_create_status()
144
+ return
145
+
146
+ # Use Rich's status for messages that should overwrite
147
+ if overwrite:
148
+ # Set current message, keep polling state so it can be concatenated
149
+ self._current_msg = message
150
+ self._update_or_create_status()
151
+ return
152
+
153
+ # Close status for normal messages
154
+ self._close_status()
155
+ logger.info(message)
@@ -1,32 +1,38 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: qoro-divi
3
- Version: 0.2.0b1
4
- Summary:
3
+ Version: 0.6.0
4
+ Summary: A Python library to automate generating, parallelizing, and executing quantum programs.
5
+ License-File: LICENSE
6
+ License-File: LICENSES/.license-header
7
+ License-File: LICENSES/Apache-2.0.txt
5
8
  Author: Ahmed Darwish
6
9
  Author-email: ahmed@qoroquantum.de
7
10
  Requires-Python: >=3.11,<3.13
8
11
  Classifier: Programming Language :: Python :: 3
9
12
  Classifier: Programming Language :: Python :: 3.11
10
13
  Classifier: Programming Language :: Python :: 3.12
11
- Requires-Dist: cirq-core (<1.5.0)
14
+ Requires-Dist: basis-set-exchange (>=0.11,<0.12)
15
+ Requires-Dist: cirq-core (>=1.6,<2.0)
12
16
  Requires-Dist: dill (>=0.4.0,<0.5.0)
17
+ Requires-Dist: dwave-hybrid (>=0.6.14,<0.7.0)
13
18
  Requires-Dist: matplotlib (>=3.10.3,<4.0.0)
14
- Requires-Dist: mitiq (>=0.46,<0.47)
19
+ Requires-Dist: mitiq (>=0.48,<0.49)
15
20
  Requires-Dist: networkx (>=3.5,<4.0)
16
- Requires-Dist: pennylane (>=0.40,<0.41)
21
+ Requires-Dist: numpy
22
+ Requires-Dist: pennylane (>=0.43,<0.44)
23
+ Requires-Dist: pennylane-qiskit (>=0.43,<0.44)
17
24
  Requires-Dist: ply (>=3.11,<4.0)
25
+ Requires-Dist: pydantic (>=2.5.0,<2.10)
18
26
  Requires-Dist: pymetis (>=2025.1.1,<2026.0.0)
19
- Requires-Dist: pyqt6 (>=6.9.1,<7.0.0)
27
+ Requires-Dist: pymoo (>=0.6,<0.7)
20
28
  Requires-Dist: python-dotenv (>=1.1.1,<2.0.0)
21
- Requires-Dist: qiskit (<2.0)
22
- Requires-Dist: qiskit-aer (>=0.17.1,<0.18.0)
23
- Requires-Dist: qiskit-algorithms (>=0.3.1,<0.4.0)
24
- Requires-Dist: qiskit-ibm-runtime (>=0.37,<0.38)
25
- Requires-Dist: qiskit-optimization (>=0.6.1,<0.7.0)
29
+ Requires-Dist: qiskit (>=2.1,<2.3)
30
+ Requires-Dist: qiskit-aer
31
+ Requires-Dist: qiskit-ibm-runtime (>=0.41,<0.42)
26
32
  Requires-Dist: requests (>=2.32.4,<3.0.0)
27
33
  Requires-Dist: rich (>=14.0.0,<15.0.0)
28
34
  Requires-Dist: scikit-learn (>=1.7.0,<2.0.0)
29
- Requires-Dist: scipy (<=1.15.2)
35
+ Requires-Dist: scipy (>=1.16,<2.0)
30
36
  Requires-Dist: sympy (>=1.14.0,<2.0.0)
31
37
  Description-Content-Type: text/markdown
32
38
 
@@ -38,6 +44,9 @@ Divi is designed to allow researchers, students, and enterprises to deploy quant
38
44
 
39
45
  ---
40
46
 
47
+ > [!IMPORTANT]
48
+ > Divi is under active development and in early stages. Users should expect frequent changes that are likely to be incompatible with previously published versions.
49
+
41
50
  ## 🚀 Features
42
51
 
43
52
  - 🧠 **Smart Job Parallelization**: Automatically parallelizes your quantum programs based on task structure.
@@ -49,9 +58,28 @@ Divi is designed to allow researchers, students, and enterprises to deploy quant
49
58
 
50
59
  ## 📦 Installation
51
60
 
52
- [TODO]
61
+ Divi can be easily installed from Pypi
62
+
63
+ ```bash
64
+ pip install qoro-divi
65
+ ```
53
66
 
54
67
  ## 📚 Documentation
55
68
 
56
- Full documentation is available at: <https://docs.qoroquantum.net/divi>
69
+ - Full documentation is available at: <https://docs.qoroquantum.net/divi>
70
+ - Tutorials can be found in the `tutorials` folder.
71
+
72
+ ## 🧪 Testing
73
+
74
+ To run the test suite:
75
+
76
+ ```bash
77
+ # Run all tests
78
+ pytest
79
+
80
+ # Run only API tests (requires API token)
81
+ pytest --run-api-tests
82
+ ```
83
+
84
+ **Note:** Some tests require a Qoro API token to test the cloud REST API. Set the `QORO_API_KEY` environment variable or use the `--api-token` option. For local development, you can create a `.env`.
57
85
 
@@ -0,0 +1,47 @@
1
+ divi/__init__.py,sha256=SyBWflbDS6qGEtHg-AfzD1TRNgfXoW2H5qTYGJ-W3XQ,167
2
+ divi/backends/__init__.py,sha256=AdWc8kKYZIfO6cL7BI1uut8DHTTxfXSDDFluxM4rsuI,484
3
+ divi/backends/_backend_properties_conversion.py,sha256=UQoKy6FNpwZexlCcCnJYymZA6UfyKSaO8SV-kYV59v8,8204
4
+ divi/backends/_circuit_runner.py,sha256=BOLLyZTVvj-YzqPDQh9tbOdD4crBqGdxAZdtgLmMGLU,2130
5
+ divi/backends/_execution_result.py,sha256=1p7l-e7Opxq7SU433fYmIMedoyO-mvH_jC927GUI7zE,2614
6
+ divi/backends/_parallel_simulator.py,sha256=vjaopHKzFoCV5gCI2z4g33REIbxx9dFkNaEmuPUuvK8,18318
7
+ divi/backends/_qoro_service.py,sha256=n_gbllNlwwgHGhlBIrjS_00qKJfKooB4HoiIzp68FJk,24418
8
+ divi/backends/_qpu_system.py,sha256=mo0SIlih5TEgJ9XdW_JFVbRP8pcNQ9jqzwK-08WzkiI,3065
9
+ divi/backends/_results_processing.py,sha256=2yakh9-kDEiW7cJvfClzE5fbgRumJZ-jGSTKdUdgHCY,3873
10
+ divi/circuits/__init__.py,sha256=ZPabkWuAnXor82rkymjgYhYC386rvu7GqvvtWSjUSmI,371
11
+ divi/circuits/_cirq/__init__.py,sha256=EtdMjq4MGwpR7GnJb5WrqhlmXEoV284QA4nM6K5bmtU,220
12
+ divi/circuits/_cirq/_parser.py,sha256=4VVUG-BQuGEyfiX3sJGUTDFZcO_awcm_FjlwWjq3D-A,3746
13
+ divi/circuits/_cirq/_qasm_export.py,sha256=32h5a-m_7tVfje6MmEgwRNPd9U0ximqddbCsOIqVg6U,2944
14
+ divi/circuits/_core.py,sha256=fVZkEPFWXVOGmvkgHU6ll2Asz8L2TZpm1Bbw89fmC-A,15898
15
+ divi/circuits/_qasm_conversion.py,sha256=-ZXVWmcetgP7N-YeSpxX6E2MhazuhdTqlyZEhPjC-tQ,9665
16
+ divi/circuits/_qasm_validation.py,sha256=A4LqllrYzzMgWVuAVdVUBlU6A99pw5qqLyWiRXkDScE,22704
17
+ divi/circuits/qem.py,sha256=o6rMPUcxLuCBitBb8-QcxveUiKZVsP3HMamxyVFLi6M,6805
18
+ divi/qprog/__init__.py,sha256=DxtUdJDHmbZuvGjEyTVKZKW5OpI-srSeCXaJMcJqHWk,834
19
+ divi/qprog/_expectation.py,sha256=2Imgdng8gU1ImVXHprMUS1YbpAY0eI19Mp08Vw5lGvU,7395
20
+ divi/qprog/_hamiltonians.py,sha256=UHiCRo1k3h3cPQcIrRYhd82n-vS78I8U1LYT6AjJkdc,9419
21
+ divi/qprog/algorithms/__init__.py,sha256=V2gWqMIaSxG0c4X9QrDv2MwXiL1PCrPfVcoLk1hPbgg,380
22
+ divi/qprog/algorithms/_ansatze.py,sha256=gSAf4S5QgRw7-WKpE_Vf6okiZ02rIvKiKWa771I_v5w,13329
23
+ divi/qprog/algorithms/_custom_vqa.py,sha256=eVMsuqqN3PQEDoleSqIdI-KUU99yIq5D_7W582b_qFg,10092
24
+ divi/qprog/algorithms/_pce.py,sha256=XQmUnVc0Gv7bn3I5qTOvcdk1gEoUXOKzTO2kycCT0OA,9870
25
+ divi/qprog/algorithms/_qaoa.py,sha256=i-li-6VYWZC_F3BO7Bt6kNyw1ifWCwNd1z08t8Drqsc,22497
26
+ divi/qprog/algorithms/_vqe.py,sha256=Hq1kXgje5mifobHeEfMe7nJ9m-RiE9AufdyAy7Krzsc,10234
27
+ divi/qprog/batch.py,sha256=1aYH0jlw2mU5jeEiZUu5sET2fpic0gsrIHw3PJ_gfSk,20622
28
+ divi/qprog/checkpointing.py,sha256=g0I5fV4LMVjZZ-780WsojlwgQmJvY6nxZILAkUrOwk0,17369
29
+ divi/qprog/exceptions.py,sha256=2VvUf8qgNBw60Q4wyt_2nbE4JHHMmZiT2JaGmWChp2o,231
30
+ divi/qprog/optimizers.py,sha256=BdjJl1Fw56RMdXiVcNedNH1uOCxdmj-en4s_cbixl5U,39104
31
+ divi/qprog/quantum_program.py,sha256=qa_tU--YrT5cmd9q5FkyRDM5rBbkNAAwQHr52g7P8nU,12174
32
+ divi/qprog/typing.py,sha256=-1xQxzrJBq1-KL2YCEFcD5yN25Vc_25Sg1FrJ-Wi9E0,2072
33
+ divi/qprog/variational_quantum_algorithm.py,sha256=n8A1YPOcCP0gdim0vwpe5iX9DP1psrYwelMiRjrQcM0,46798
34
+ divi/qprog/workflows/__init__.py,sha256=_GAFsZsgj9p61E1xUXasa1aspwcOWp4s8i6hA6mQ9eg,320
35
+ divi/qprog/workflows/_graph_partitioning.py,sha256=nTxpPom7Aulfndlqws0FkaTvs1siRCeixiBkwRqV1Xc,24435
36
+ divi/qprog/workflows/_qubo_partitioning.py,sha256=JHF1m7MI3-BLeG4rYJ9bPelFd_KIAFY2Ihyy3IZV6K8,8431
37
+ divi/qprog/workflows/_vqe_sweep.py,sha256=rT79jYex04linD42mdXCM54LdZ83eFCwUGAKnX1PQSM,19658
38
+ divi/reporting/__init__.py,sha256=gaBUZrhNxR53VHojZxfjvQRDl-eDHo901vLE8I95kIw,290
39
+ divi/reporting/_pbar.py,sha256=of9vI37puHWVPCUwRbwtiT1cDQVCpUbpbxNx__hF7mE,4180
40
+ divi/reporting/_qlogger.py,sha256=d16eT_n8Me83Qn1QOFmzEoVnRYTePM5l_I1GeNVCo4o,1990
41
+ divi/reporting/_reporter.py,sha256=KlYgKuNl-3rkAqgi8PQRutvtHyoMDezR_Zfn4LhJTnU,5492
42
+ qoro_divi-0.6.0.dist-info/METADATA,sha256=CP5PvpfmWFC4_ie2i36oJDhKdIg3tQttw6rIZkTUxgQ,2992
43
+ qoro_divi-0.6.0.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
44
+ qoro_divi-0.6.0.dist-info/licenses/LICENSE,sha256=NS4JlQrgNwg1bvB3kE5shE-P4cJgnntgl-kClbOpG_Q,10760
45
+ qoro_divi-0.6.0.dist-info/licenses/LICENSES/.license-header,sha256=2jN_xtJscqP8LG-NaveY2KHUkfRCC543Y_XjOyKEfWY,105
46
+ qoro_divi-0.6.0.dist-info/licenses/LICENSES/Apache-2.0.txt,sha256=yoILHpvVuguUBpk8UwMnzJbcHUUyst9iGNNuEwUtWVc,10270
47
+ qoro_divi-0.6.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 1.9.1
2
+ Generator: poetry-core 2.2.1
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -0,0 +1,3 @@
1
+ SPDX-FileCopyrightText: 2025 Qoro Quantum Ltd <divi@qoroquantum.de>
2
+
3
+ SPDX-License-Identifier: Apache-2.0
divi/_pbar.py DELETED
@@ -1,73 +0,0 @@
1
- # SPDX-FileCopyrightText: 2025 Qoro Quantum Ltd <divi@qoroquantum.de>
2
- #
3
- # SPDX-License-Identifier: Apache-2.0
4
-
5
- from typing import Optional
6
-
7
- from rich.progress import (
8
- BarColumn,
9
- MofNCompleteColumn,
10
- Progress,
11
- ProgressColumn,
12
- SpinnerColumn,
13
- TextColumn,
14
- )
15
- from rich.text import Text
16
-
17
-
18
- class ConditionalSpinnerColumn(ProgressColumn):
19
- def __init__(self):
20
- super().__init__()
21
- self.spinner = SpinnerColumn("point")
22
-
23
- def render(self, task):
24
- status = task.fields.get("final_status")
25
-
26
- if status in ("Success", "Failed"):
27
- return Text("")
28
-
29
- return self.spinner.render(task)
30
-
31
-
32
- class PhaseStatusColumn(ProgressColumn):
33
- def __init__(self, max_retries: int, table_column=None):
34
- super().__init__(table_column)
35
-
36
- self._max_retries = max_retries
37
- self._last_message = ""
38
-
39
- def render(self, task):
40
- final_status = task.fields.get("final_status")
41
-
42
- if final_status == "Success":
43
- return Text("• Success! ✅", style="bold green")
44
- elif final_status == "Failed":
45
- return Text("• Failed! ❌", style="bold red")
46
-
47
- message = task.fields.get("message")
48
- if message != "":
49
- self._last_message = message
50
-
51
- poll_attempt = task.fields.get("poll_attempt")
52
- if poll_attempt > 0:
53
- return Text(
54
- f"[{self._last_message}] Polling {poll_attempt}/{self._max_retries}"
55
- )
56
-
57
- return Text(f"[{self._last_message}]")
58
-
59
-
60
- def make_progress_bar(
61
- max_retries: Optional[int] = None, is_jupyter: bool = False
62
- ) -> Progress:
63
- return Progress(
64
- TextColumn("[bold blue]{task.fields[job_name]}"),
65
- BarColumn(),
66
- MofNCompleteColumn(),
67
- ConditionalSpinnerColumn(),
68
- PhaseStatusColumn(max_retries=max_retries),
69
- # For jupyter notebooks, refresh manually instead
70
- auto_refresh=not is_jupyter,
71
- # Give a dummy positive value if is_jupyter
72
- refresh_per_second=10 if not is_jupyter else 999,
73
- )
divi/circuits.py DELETED
@@ -1,139 +0,0 @@
1
- # SPDX-FileCopyrightText: 2025 Qoro Quantum Ltd <divi@qoroquantum.de>
2
- #
3
- # SPDX-License-Identifier: Apache-2.0
4
-
5
- import re
6
- from copy import deepcopy
7
- from itertools import product
8
- from typing import Literal, Optional
9
-
10
- import dill
11
- import pennylane as qml
12
- from pennylane.transforms.core.transform_program import TransformProgram
13
- from qiskit.qasm2 import dumps
14
-
15
- from divi.qasm import to_openqasm
16
- from divi.qem import QEMProtocol
17
-
18
- TRANSFORM_PROGRAM = TransformProgram()
19
- TRANSFORM_PROGRAM.add_transform(qml.transforms.split_to_single_terms)
20
- TRANSFORM_PROGRAM.add_transform(qml.transforms.split_non_commuting)
21
-
22
-
23
- class Circuit:
24
- _id_counter = 0
25
-
26
- def __init__(
27
- self,
28
- main_circuit,
29
- tags: list[str],
30
- qasm_circuits: list[str] = None,
31
- ):
32
- self.main_circuit = main_circuit
33
- self.circuit_type = main_circuit.__module__.split(".")[0]
34
- self.tags = tags
35
-
36
- self.qasm_circuits = qasm_circuits
37
-
38
- if self.qasm_circuits is None:
39
- self.convert_to_qasm()
40
-
41
- self.circuit_id = Circuit._id_counter
42
- Circuit._id_counter += 1
43
-
44
- def __str__(self):
45
- return f"Circuit: {self.circuit_id}"
46
-
47
- def convert_to_qasm(self):
48
- if self.circuit_type == "pennylane":
49
- self.qasm_circuits = to_openqasm(
50
- self.main_circuit,
51
- measurement_groups=[self.main_circuit.measurements],
52
- return_measurements_separately=False,
53
- )
54
-
55
- elif self.circuit_type == "qiskit":
56
- self.qasm_circuits = [dumps(self.main_circuit)]
57
-
58
- else:
59
- raise ValueError(
60
- f"Invalid circuit type. Circuit type {self.circuit_type} not currently supported."
61
- )
62
-
63
-
64
- class MetaCircuit:
65
- def __init__(
66
- self,
67
- main_circuit,
68
- symbols,
69
- grouping_strategy: Optional[Literal["wires", "default", "qwc"]] = None,
70
- qem_protocol: Optional[QEMProtocol] = None,
71
- ):
72
- self.main_circuit = main_circuit
73
- self.symbols = symbols
74
- self.qem_protocol = qem_protocol
75
-
76
- transform_program = deepcopy(TRANSFORM_PROGRAM)
77
- transform_program[1].kwargs["grouping_strategy"] = grouping_strategy
78
-
79
- qscripts, self.postprocessing_fn = transform_program((main_circuit,))
80
-
81
- self.compiled_circuits_bodies, self.measurements = to_openqasm(
82
- main_circuit,
83
- measurement_groups=[qsc.measurements for qsc in qscripts],
84
- return_measurements_separately=True,
85
- # TODO: optimize later
86
- measure_all=True,
87
- symbols=self.symbols,
88
- qem_protocol=qem_protocol,
89
- )
90
-
91
- # Need to store the measurement groups for computing
92
- # expectation values later on, stripped of the `qml.expval` wrapper
93
- self.measurement_groups = [
94
- [meas.obs for meas in qsc.measurements] for qsc in qscripts
95
- ]
96
-
97
- def __getstate__(self):
98
- state = self.__dict__.copy()
99
- state["postprocessing_fn"] = dill.dumps(self.postprocessing_fn)
100
- return state
101
-
102
- def __setstate__(self, state):
103
- state["postprocessing_fn"] = dill.loads(state["postprocessing_fn"])
104
-
105
- self.__dict__.update(state)
106
-
107
- def initialize_circuit_from_params(
108
- self, param_list, tag_prefix: str = "", precision: int = 8
109
- ) -> Circuit:
110
- mapping = dict(
111
- zip(
112
- map(lambda x: re.escape(str(x)), self.symbols),
113
- map(lambda x: f"{x:.{precision}f}", param_list),
114
- )
115
- )
116
- pattern = re.compile("|".join(k for k in mapping.keys()))
117
-
118
- final_qasm_strs = []
119
- for circuit_body in self.compiled_circuits_bodies:
120
- final_qasm_strs.append(
121
- pattern.sub(lambda match: mapping[match.group(0)], circuit_body)
122
- )
123
-
124
- tags = []
125
- qasm_circuits = []
126
- for (i, body_str), (j, meas_str) in product(
127
- enumerate(final_qasm_strs), enumerate(self.measurements)
128
- ):
129
- qasm_circuits.append(body_str + meas_str)
130
-
131
- nonempty_subtags = filter(
132
- None,
133
- [tag_prefix, f"{self.qem_protocol.name}:{i}", str(j)],
134
- )
135
- tags.append("_".join(nonempty_subtags))
136
-
137
- # Note: The main circuit's parameters are still in symbol form.
138
- # Not sure if it is necessary for any useful application to parameterize them.
139
- return Circuit(self.main_circuit, qasm_circuits=qasm_circuits, tags=tags)