qoro-divi 0.2.0b1__py3-none-any.whl → 0.5.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 (88) hide show
  1. divi/__init__.py +1 -2
  2. divi/backends/__init__.py +9 -0
  3. divi/backends/_circuit_runner.py +70 -0
  4. divi/backends/_execution_result.py +70 -0
  5. divi/backends/_parallel_simulator.py +486 -0
  6. divi/backends/_qoro_service.py +663 -0
  7. divi/backends/_qpu_system.py +101 -0
  8. divi/backends/_results_processing.py +133 -0
  9. divi/circuits/__init__.py +8 -0
  10. divi/{exp/cirq → circuits/_cirq}/__init__.py +1 -2
  11. divi/circuits/_cirq/_parser.py +110 -0
  12. divi/circuits/_cirq/_qasm_export.py +78 -0
  13. divi/circuits/_core.py +369 -0
  14. divi/{qasm.py → circuits/_qasm_conversion.py} +73 -14
  15. divi/circuits/_qasm_validation.py +694 -0
  16. divi/qprog/__init__.py +24 -6
  17. divi/qprog/_expectation.py +181 -0
  18. divi/qprog/_hamiltonians.py +281 -0
  19. divi/qprog/algorithms/__init__.py +14 -0
  20. divi/qprog/algorithms/_ansatze.py +356 -0
  21. divi/qprog/algorithms/_qaoa.py +572 -0
  22. divi/qprog/algorithms/_vqe.py +249 -0
  23. divi/qprog/batch.py +383 -73
  24. divi/qprog/checkpointing.py +556 -0
  25. divi/qprog/exceptions.py +9 -0
  26. divi/qprog/optimizers.py +1014 -43
  27. divi/qprog/quantum_program.py +231 -413
  28. divi/qprog/variational_quantum_algorithm.py +995 -0
  29. divi/qprog/workflows/__init__.py +10 -0
  30. divi/qprog/{_graph_partitioning.py → workflows/_graph_partitioning.py} +139 -95
  31. divi/qprog/workflows/_qubo_partitioning.py +220 -0
  32. divi/qprog/workflows/_vqe_sweep.py +560 -0
  33. divi/reporting/__init__.py +7 -0
  34. divi/reporting/_pbar.py +127 -0
  35. divi/reporting/_qlogger.py +68 -0
  36. divi/reporting/_reporter.py +133 -0
  37. {qoro_divi-0.2.0b1.dist-info → qoro_divi-0.5.0.dist-info}/METADATA +43 -15
  38. qoro_divi-0.5.0.dist-info/RECORD +43 -0
  39. {qoro_divi-0.2.0b1.dist-info → qoro_divi-0.5.0.dist-info}/WHEEL +1 -1
  40. qoro_divi-0.5.0.dist-info/licenses/LICENSES/.license-header +3 -0
  41. divi/_pbar.py +0 -73
  42. divi/circuits.py +0 -139
  43. divi/exp/cirq/_lexer.py +0 -126
  44. divi/exp/cirq/_parser.py +0 -889
  45. divi/exp/cirq/_qasm_export.py +0 -37
  46. divi/exp/cirq/_qasm_import.py +0 -35
  47. divi/exp/cirq/exception.py +0 -21
  48. divi/exp/scipy/_cobyla.py +0 -342
  49. divi/exp/scipy/pyprima/LICENCE.txt +0 -28
  50. divi/exp/scipy/pyprima/__init__.py +0 -263
  51. divi/exp/scipy/pyprima/cobyla/__init__.py +0 -0
  52. divi/exp/scipy/pyprima/cobyla/cobyla.py +0 -599
  53. divi/exp/scipy/pyprima/cobyla/cobylb.py +0 -849
  54. divi/exp/scipy/pyprima/cobyla/geometry.py +0 -240
  55. divi/exp/scipy/pyprima/cobyla/initialize.py +0 -269
  56. divi/exp/scipy/pyprima/cobyla/trustregion.py +0 -540
  57. divi/exp/scipy/pyprima/cobyla/update.py +0 -331
  58. divi/exp/scipy/pyprima/common/__init__.py +0 -0
  59. divi/exp/scipy/pyprima/common/_bounds.py +0 -41
  60. divi/exp/scipy/pyprima/common/_linear_constraints.py +0 -46
  61. divi/exp/scipy/pyprima/common/_nonlinear_constraints.py +0 -64
  62. divi/exp/scipy/pyprima/common/_project.py +0 -224
  63. divi/exp/scipy/pyprima/common/checkbreak.py +0 -107
  64. divi/exp/scipy/pyprima/common/consts.py +0 -48
  65. divi/exp/scipy/pyprima/common/evaluate.py +0 -101
  66. divi/exp/scipy/pyprima/common/history.py +0 -39
  67. divi/exp/scipy/pyprima/common/infos.py +0 -30
  68. divi/exp/scipy/pyprima/common/linalg.py +0 -452
  69. divi/exp/scipy/pyprima/common/message.py +0 -336
  70. divi/exp/scipy/pyprima/common/powalg.py +0 -131
  71. divi/exp/scipy/pyprima/common/preproc.py +0 -393
  72. divi/exp/scipy/pyprima/common/present.py +0 -5
  73. divi/exp/scipy/pyprima/common/ratio.py +0 -56
  74. divi/exp/scipy/pyprima/common/redrho.py +0 -49
  75. divi/exp/scipy/pyprima/common/selectx.py +0 -346
  76. divi/interfaces.py +0 -25
  77. divi/parallel_simulator.py +0 -258
  78. divi/qlogger.py +0 -119
  79. divi/qoro_service.py +0 -343
  80. divi/qprog/_mlae.py +0 -182
  81. divi/qprog/_qaoa.py +0 -440
  82. divi/qprog/_vqe.py +0 -275
  83. divi/qprog/_vqe_sweep.py +0 -144
  84. divi/utils.py +0 -116
  85. qoro_divi-0.2.0b1.dist-info/RECORD +0 -58
  86. /divi/{qem.py → circuits/qem.py} +0 -0
  87. {qoro_divi-0.2.0b1.dist-info → qoro_divi-0.5.0.dist-info/licenses}/LICENSE +0 -0
  88. {qoro_divi-0.2.0b1.dist-info → qoro_divi-0.5.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,133 @@
1
+ # SPDX-FileCopyrightText: 2025 Qoro Quantum Ltd <divi@qoroquantum.de>
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ import logging
6
+ from abc import ABC, abstractmethod
7
+ from queue import Queue
8
+
9
+ from rich.console import Console
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+
14
+ class ProgressReporter(ABC):
15
+ """An abstract base class for reporting progress of a quantum program."""
16
+
17
+ @abstractmethod
18
+ def update(self, **kwargs) -> None:
19
+ """Provides a progress update."""
20
+ pass
21
+
22
+ @abstractmethod
23
+ def info(self, message: str, **kwargs) -> None:
24
+ """Provides a simple informational message.
25
+
26
+ Args:
27
+ message: The message to display.
28
+ **kwargs: Additional keyword arguments for subclasses.
29
+ """
30
+ pass
31
+
32
+
33
+ class QueueProgressReporter(ProgressReporter):
34
+ """Reports progress by putting structured dictionaries onto a Queue."""
35
+
36
+ def __init__(self, job_id: str, progress_queue: Queue):
37
+ self._job_id = job_id
38
+ self._queue = progress_queue
39
+
40
+ def update(self, **kwargs):
41
+ payload = {"job_id": self._job_id, "progress": 1}
42
+ self._queue.put(payload)
43
+
44
+ def info(self, message: str, **kwargs):
45
+ payload = {"job_id": self._job_id, "progress": 0, "message": message}
46
+
47
+ if "Finished successfully!" in message:
48
+ payload["final_status"] = "Success"
49
+
50
+ if "poll_attempt" in kwargs:
51
+ # For polling, remove the message key so the last message persists.
52
+ del payload["message"]
53
+ payload["poll_attempt"] = kwargs["poll_attempt"]
54
+ payload["max_retries"] = kwargs["max_retries"]
55
+ payload["service_job_id"] = kwargs["service_job_id"]
56
+ payload["job_status"] = kwargs["job_status"]
57
+ else:
58
+ # For any other message, explicitly reset the polling attempt counter.
59
+ payload["poll_attempt"] = 0
60
+
61
+ self._queue.put(payload)
62
+
63
+
64
+ class LoggingProgressReporter(ProgressReporter):
65
+ """Reports progress by logging messages to the console."""
66
+
67
+ def __init__(self):
68
+ # Use the same console instance that RichHandler uses to avoid interference
69
+ self._console = Console(file=None) # file=None uses stdout, same as RichHandler
70
+ self._status = None # Track active status for overwriting messages
71
+ self._current_msg = None # Track current main message
72
+ self._polling_msg = None # Track current polling message
73
+
74
+ def _close_status(self):
75
+ """Close any active status."""
76
+ if self._status:
77
+ self._status.__exit__(None, None, None)
78
+ self._status = None
79
+ self._current_msg = None
80
+ self._polling_msg = None
81
+
82
+ def _build_status_msg(self) -> str:
83
+ """Build combined status message from current message and polling info."""
84
+ parts = []
85
+ if self._current_msg:
86
+ parts.append(self._current_msg)
87
+ if self._polling_msg:
88
+ parts.append(self._polling_msg)
89
+ return " - ".join(parts) if parts else ""
90
+
91
+ def _update_or_create_status(self):
92
+ """Update existing status or create a new one with combined message."""
93
+ status_msg = self._build_status_msg()
94
+ if not status_msg:
95
+ return
96
+ if self._status:
97
+ self._status.update(status_msg)
98
+ else:
99
+ self._status = self._console.status(status_msg, spinner="aesthetic")
100
+ self._status.__enter__()
101
+
102
+ def update(self, **kwargs):
103
+ # Close any active status before logging
104
+ self._close_status()
105
+ logger.info(f"Finished Iteration #{kwargs['iteration']}")
106
+
107
+ def info(self, message: str, overwrite: bool = False, **kwargs):
108
+ # A special check for iteration updates to use Rich's status for overwriting
109
+ if "poll_attempt" in kwargs:
110
+ self._polling_msg = (
111
+ f"Job [cyan]{kwargs['service_job_id'].split('-')[0]}[/cyan] is "
112
+ f"{kwargs['job_status']}. Polling attempt {kwargs['poll_attempt']} / "
113
+ f"{kwargs['max_retries']}"
114
+ )
115
+ self._update_or_create_status()
116
+ return
117
+
118
+ # Use Rich's status for iteration messages to enable overwriting
119
+ if "iteration" in kwargs:
120
+ self._current_msg = f"Iteration #{kwargs['iteration'] + 1}: {message}"
121
+ self._update_or_create_status()
122
+ return
123
+
124
+ # Use Rich's status for messages that should overwrite
125
+ if overwrite:
126
+ # Set current message, keep polling state so it can be concatenated
127
+ self._current_msg = message
128
+ self._update_or_create_status()
129
+ return
130
+
131
+ # Close status for normal messages
132
+ self._close_status()
133
+ logger.info(message)
@@ -1,33 +1,39 @@
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.5.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.42.3,<0.43.0)
17
23
  Requires-Dist: ply (>=3.11,<4.0)
24
+ Requires-Dist: pydantic (>=2.5.0,<2.10)
18
25
  Requires-Dist: pymetis (>=2025.1.1,<2026.0.0)
19
- Requires-Dist: pyqt6 (>=6.9.1,<7.0.0)
26
+ Requires-Dist: pymoo (>=0.6,<0.7)
20
27
  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)
28
+ Requires-Dist: qiskit (>=2.2,<3.0)
29
+ Requires-Dist: qiskit-aer
30
+ Requires-Dist: qiskit-ibm-runtime (>0.42)
26
31
  Requires-Dist: requests (>=2.32.4,<3.0.0)
27
32
  Requires-Dist: rich (>=14.0.0,<15.0.0)
28
33
  Requires-Dist: scikit-learn (>=1.7.0,<2.0.0)
29
- Requires-Dist: scipy (<=1.15.2)
34
+ Requires-Dist: scipy (>=1.16,<2.0)
30
35
  Requires-Dist: sympy (>=1.14.0,<2.0.0)
36
+ Requires-Dist: tuna (>=0.5.11,<0.6.0)
31
37
  Description-Content-Type: text/markdown
32
38
 
33
39
  # Divi
@@ -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,43 @@
1
+ divi/__init__.py,sha256=SyBWflbDS6qGEtHg-AfzD1TRNgfXoW2H5qTYGJ-W3XQ,167
2
+ divi/backends/__init__.py,sha256=4catD73mnCHd6EHpo0vZkR-MLnZeCBFC7Xn6VnMUFlc,404
3
+ divi/backends/_circuit_runner.py,sha256=BOLLyZTVvj-YzqPDQh9tbOdD4crBqGdxAZdtgLmMGLU,2130
4
+ divi/backends/_execution_result.py,sha256=1p7l-e7Opxq7SU433fYmIMedoyO-mvH_jC927GUI7zE,2614
5
+ divi/backends/_parallel_simulator.py,sha256=p3BLvX7Y1gY2-rMDEes4uKutUgN1Fj0PqZZ4DGnlWsA,18271
6
+ divi/backends/_qoro_service.py,sha256=n_gbllNlwwgHGhlBIrjS_00qKJfKooB4HoiIzp68FJk,24418
7
+ divi/backends/_qpu_system.py,sha256=mo0SIlih5TEgJ9XdW_JFVbRP8pcNQ9jqzwK-08WzkiI,3065
8
+ divi/backends/_results_processing.py,sha256=2yakh9-kDEiW7cJvfClzE5fbgRumJZ-jGSTKdUdgHCY,3873
9
+ divi/circuits/__init__.py,sha256=XcQVYHb8HakdrANwGwBgM40EotVZI6DNtCncGx40r6Y,328
10
+ divi/circuits/_cirq/__init__.py,sha256=EtdMjq4MGwpR7GnJb5WrqhlmXEoV284QA4nM6K5bmtU,220
11
+ divi/circuits/_cirq/_parser.py,sha256=4VVUG-BQuGEyfiX3sJGUTDFZcO_awcm_FjlwWjq3D-A,3746
12
+ divi/circuits/_cirq/_qasm_export.py,sha256=32h5a-m_7tVfje6MmEgwRNPd9U0ximqddbCsOIqVg6U,2944
13
+ divi/circuits/_core.py,sha256=Zg_3TcxzboYoxDj5Sz0WzPP7KVU_FxrfJD6Hqk2s9pU,15310
14
+ divi/circuits/_qasm_conversion.py,sha256=-ZXVWmcetgP7N-YeSpxX6E2MhazuhdTqlyZEhPjC-tQ,9665
15
+ divi/circuits/_qasm_validation.py,sha256=A4LqllrYzzMgWVuAVdVUBlU6A99pw5qqLyWiRXkDScE,22704
16
+ divi/circuits/qem.py,sha256=o6rMPUcxLuCBitBb8-QcxveUiKZVsP3HMamxyVFLi6M,6805
17
+ divi/qprog/__init__.py,sha256=y1vTwQWmtBooD7JS_LjKIXVp8WIc1MeOtoBsFdodmlE,809
18
+ divi/qprog/_expectation.py,sha256=2Imgdng8gU1ImVXHprMUS1YbpAY0eI19Mp08Vw5lGvU,7395
19
+ divi/qprog/_hamiltonians.py,sha256=UHiCRo1k3h3cPQcIrRYhd82n-vS78I8U1LYT6AjJkdc,9419
20
+ divi/qprog/algorithms/__init__.py,sha256=KLGD3zRk3z4nJQDqBjejTGgJ5m0n02jyBVHLJihMMws,355
21
+ divi/qprog/algorithms/_ansatze.py,sha256=kks5SZ2fbGAX1hMdiuBfn8-45ocvFzJMLXpdnaOtVwg,12766
22
+ divi/qprog/algorithms/_qaoa.py,sha256=wRESmK4llPGOluM0M67FdB-kdPIFQUCurmjR4novAYU,21903
23
+ divi/qprog/algorithms/_vqe.py,sha256=kh-olGVMfoiaaMutxgbfi4SaHUXhIOcqW8ksElNllkQ,9571
24
+ divi/qprog/batch.py,sha256=qNVb9Y751NLI5zkVmuN8MSIvi-cQmwU7yS_yjG2acgI,20431
25
+ divi/qprog/checkpointing.py,sha256=g0I5fV4LMVjZZ-780WsojlwgQmJvY6nxZILAkUrOwk0,17369
26
+ divi/qprog/exceptions.py,sha256=2VvUf8qgNBw60Q4wyt_2nbE4JHHMmZiT2JaGmWChp2o,231
27
+ divi/qprog/optimizers.py,sha256=BdjJl1Fw56RMdXiVcNedNH1uOCxdmj-en4s_cbixl5U,39104
28
+ divi/qprog/quantum_program.py,sha256=daPEZRM9Z0JwHVUjQx560yaowj8ZFKLdEoqFqZsfxt4,11678
29
+ divi/qprog/variational_quantum_algorithm.py,sha256=ijC7z5ednw48sEed8AzkdEA0q-x0GL0zfq7Og3pB2mQ,37874
30
+ divi/qprog/workflows/__init__.py,sha256=_GAFsZsgj9p61E1xUXasa1aspwcOWp4s8i6hA6mQ9eg,320
31
+ divi/qprog/workflows/_graph_partitioning.py,sha256=nTxpPom7Aulfndlqws0FkaTvs1siRCeixiBkwRqV1Xc,24435
32
+ divi/qprog/workflows/_qubo_partitioning.py,sha256=hp4c5YYyLyFkkbsyIyBxpK8cDzo4Pwf-5-i23fQpjUQ,8397
33
+ divi/qprog/workflows/_vqe_sweep.py,sha256=rT79jYex04linD42mdXCM54LdZ83eFCwUGAKnX1PQSM,19658
34
+ divi/reporting/__init__.py,sha256=gaBUZrhNxR53VHojZxfjvQRDl-eDHo901vLE8I95kIw,290
35
+ divi/reporting/_pbar.py,sha256=of9vI37puHWVPCUwRbwtiT1cDQVCpUbpbxNx__hF7mE,4180
36
+ divi/reporting/_qlogger.py,sha256=d16eT_n8Me83Qn1QOFmzEoVnRYTePM5l_I1GeNVCo4o,1990
37
+ divi/reporting/_reporter.py,sha256=MLDShH8wWIL7WTeBLWYa4iiTVe-_P6lU-kqeVcq2Qm0,4755
38
+ qoro_divi-0.5.0.dist-info/METADATA,sha256=i84uZ0ap0FNvL2a5YyCjjcbcvZ44JbdUt3y4MrP_X3g,2980
39
+ qoro_divi-0.5.0.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
40
+ qoro_divi-0.5.0.dist-info/licenses/LICENSE,sha256=NS4JlQrgNwg1bvB3kE5shE-P4cJgnntgl-kClbOpG_Q,10760
41
+ qoro_divi-0.5.0.dist-info/licenses/LICENSES/.license-header,sha256=2jN_xtJscqP8LG-NaveY2KHUkfRCC543Y_XjOyKEfWY,105
42
+ qoro_divi-0.5.0.dist-info/licenses/LICENSES/Apache-2.0.txt,sha256=yoILHpvVuguUBpk8UwMnzJbcHUUyst9iGNNuEwUtWVc,10270
43
+ qoro_divi-0.5.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)
divi/exp/cirq/_lexer.py DELETED
@@ -1,126 +0,0 @@
1
- # Copyright 2018 The Cirq Developers
2
- #
3
- # Licensed under the Apache License, Version 2.0 (the "License");
4
- # you may not use this file except in compliance with the License.
5
- # You may obtain a copy of the License at
6
- #
7
- # https://www.apache.org/licenses/LICENSE-2.0
8
- #
9
- # Unless required by applicable law or agreed to in writing, software
10
- # distributed under the License is distributed on an "AS IS" BASIS,
11
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- # See the License for the specific language governing permissions and
13
- # limitations under the License.
14
-
15
- from __future__ import annotations
16
-
17
- import re
18
-
19
- import ply.lex as lex
20
-
21
- from .exception import QasmException
22
-
23
-
24
- class QasmLexer:
25
- def __init__(self):
26
- self.lex = lex.lex(object=self, debug=False)
27
-
28
- literals = "{}[]();,+/*-^="
29
-
30
- reserved = {
31
- "qubit": "QUBIT",
32
- "qreg": "QREG",
33
- "bit": "BIT",
34
- "creg": "CREG",
35
- "measure": "MEASURE",
36
- "reset": "RESET",
37
- "gate": "GATE",
38
- "if": "IF",
39
- "pi": "PI",
40
- "input": "INPUT", # <-- Add this
41
- "angle": "ANGLE", # <-- Add this
42
- }
43
-
44
- tokens = [
45
- "FORMAT_SPEC",
46
- "NUMBER",
47
- "NATURAL_NUMBER",
48
- "STDGATESINC",
49
- "QELIBINC",
50
- "ID",
51
- "ARROW",
52
- "EQ",
53
- ] + list(reserved.values())
54
-
55
- def t_newline(self, t):
56
- r"""\n+"""
57
- t.lexer.lineno += len(t.value)
58
-
59
- t_ignore = " \t"
60
-
61
- # all numbers except NATURAL_NUMBERs:
62
- # it's useful to have this separation to be able to handle indices
63
- # separately. In case of the parameter expressions, we are "OR"-ing
64
- # them together (see p_term in _parser.py)
65
- def t_NUMBER(self, t):
66
- r"""(
67
- (
68
- [0-9]+\.?|
69
- [0-9]?\.[0-9]+
70
- )
71
- [eE][+-]?[0-9]+
72
- )|
73
- (
74
- ([0-9]+)?\.[0-9]+|
75
- [0-9]+\.)"""
76
- t.value = float(t.value)
77
- return t
78
-
79
- def t_NATURAL_NUMBER(self, t):
80
- r"""\d+"""
81
- t.value = int(t.value)
82
- return t
83
-
84
- def t_FORMAT_SPEC(self, t):
85
- r"""OPENQASM(\s+)([^\s\t\;]*);"""
86
- match = re.match(r"""OPENQASM(\s+)([^\s\t;]*);""", t.value)
87
- t.value = match.groups()[1]
88
- return t
89
-
90
- def t_QELIBINC(self, t):
91
- r"""include(\s+)"qelib1.inc";"""
92
- return t
93
-
94
- def t_STDGATESINC(self, t):
95
- r"""include(\s+)"stdgates.inc";"""
96
- return t
97
-
98
- def t_ARROW(self, t):
99
- """->"""
100
- return t
101
-
102
- def t_EQ(self, t):
103
- """=="""
104
- return t
105
-
106
- def t_ID(self, t):
107
- r"""[^\W\d_][\w_]*"""
108
- # This regex matches any Unicode letter (not digit/underscore) at the start,
109
- # followed by any number of Unicode word characters or underscores.
110
- if t.value in QasmLexer.reserved:
111
- t.type = QasmLexer.reserved[t.value]
112
- return t
113
-
114
- t_ID.__doc__ = r"[^\W\d_][\w_]*"
115
-
116
- def t_COMMENT(self, t):
117
- r"""//.*"""
118
-
119
- def t_error(self, t):
120
- raise QasmException(f"Illegal character '{t.value[0]}' at line {t.lineno}")
121
-
122
- def input(self, qasm):
123
- self.lex.input(qasm)
124
-
125
- def token(self) -> lex.Token | None:
126
- return self.lex.token()