Skip to content

Commit

Permalink
Merge branch 'main' into unary_iteration
Browse files Browse the repository at this point in the history
  • Loading branch information
tanujkhattar authored Nov 25, 2024
2 parents ddb62bd + 6c8e772 commit 96a46ea
Show file tree
Hide file tree
Showing 305 changed files with 15,595 additions and 3,707 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ jobs:
pip install --no-deps -e .
- run: |
python dev_tools/execute-notebooks.py
env:
NUMBA_NUM_THREADS: 4
format:
runs-on: ubuntu-latest
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ dmypy.json
mod_exp_bloq.json
mod_exp_cbloq.json
musical_score_example.json
ui_export/

# Jupyter
jupyter_kernel.lock
811 changes: 6 additions & 805 deletions dev_tools/autogenerate-bloqs-notebooks-v2.py

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dev_tools/bibliography.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@
"metadata": {},
"outputs": [],
"source": [
"for url, clss in backrefs.items():\n",
"for url, clss in sorted(backrefs.items(), key=lambda x: -len(x[1])):\n",
" text = f'### [{titles[url]}]({url})\\n'\n",
" for cls in clss:\n",
" text += f' - {cls.__name__}\\n'\n",
Expand Down
4 changes: 4 additions & 0 deletions dev_tools/conf/.pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ score=no
reports=no
py-version=3.9
disable=
abstract-class-instantiated, # TODO: #1440 - enable and fix
deprecated-class, # TODO: #1440 - enable and fix
possibly-used-before-assignment, # TODO: #1440 - enable and fix
used-before-assignment, # TODO: #1440 - enable and fix
C,
R,
missing-raises-doc,
Expand Down
890 changes: 890 additions & 0 deletions dev_tools/qualtran_dev_tools/notebook_specs.py

Large diffs are not rendered by default.

19 changes: 14 additions & 5 deletions dev_tools/qualtran_dev_tools/parse_docstrings.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,19 +109,28 @@ def _template(name, desc_lines):
]


def get_markdown_docstring_lines(cls: Type) -> List[str]:
def get_markdown_docstring(cls: Type) -> List[str]:
"""From a class `cls`, return its docstring as Markdown lines."""

# 1. Sphinx incantation
config = Config()
docstring = cls.__doc__ if cls.__doc__ else ""
gds = _GoogleDocstringToMarkdown(inspect.cleandoc(docstring), config=config, what='class')

# 2. Pre-pend a header.
lines = [f'## `{cls.__name__}`'] + gds.lines()
# 2. Substitute restructured text inline-code blocks to markdown-style backticks.
lines = [re.sub(r':py:func:`(\w+)`', r'`\1`', line) for line in gds.lines()]

return lines


# 3. Substitute restructured text inline-code blocks to markdown-style backticks.
lines = [re.sub(r':py:func:`(\w+)`', r'`\1`', line) for line in lines]
def get_markdown_docstring_lines(cls: Type) -> List[str]:
"""From a class `cls`, return its docstring as Markdown lines with a header."""

# 1. Get documentation lines
lines = get_markdown_docstring(cls)

# 2. Pre-pend a header.
lines = [f'## `{cls.__name__}`'] + lines

return lines

Expand Down
25 changes: 13 additions & 12 deletions dev_tools/qualtran_dev_tools/pylint_copyright_checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,24 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from __future__ import annotations

from typing import TYPE_CHECKING

from astroid import nodes
from pylint.checkers import BaseChecker
from pylint.interfaces import IRawChecker
from pylint.checkers import BaseRawFileChecker

if TYPE_CHECKING:
from pylint.lint import PyLinter


class CopyrightChecker(BaseChecker):
r"""Check for the copyright notices at the beginning of a Python source file.
class CopyrightChecker(BaseRawFileChecker):
"""Check for the copyright notices at the beginning of a Python source file.
This checker can be disabled by putting `# pylint: disable=wrong-or-nonexistent-copyright-notice`
at the beginning of a file.
"""

__implements__ = IRawChecker

# The priority must be negative. Pylint runs plugins with smaller priorities first.
priority = -1

name = "copyright-notice"
msgs = {
"R0001": (
Expand All @@ -40,7 +41,7 @@ class CopyrightChecker(BaseChecker):
options = ()

def process_module(self, node: nodes.Module) -> None:
r"""Check whether the copyright notice is correctly placed in the source file of a module.
"""Check whether the copyright notice is correctly placed in the source file of a module.
Compare the first lines of a source file against the standard copyright notice (i.e., the
`golden` variable below). Suffix whitespace (including newline symbols) is not considered
Expand Down Expand Up @@ -108,8 +109,8 @@ def skip_shebang(stream):
return


def register(linter):
r"""Register this checker to pylint.
def register(linter: PyLinter):
"""Register this checker to pylint.
The registration is done automatically if this file is in $PYTHONPATH.
"""
Expand Down
2 changes: 1 addition & 1 deletion dev_tools/qualtran_dev_tools/reference_docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ def apply_fixups(reporoot: Path):
output_dir = reporoot / 'docs/reference'
remove_extra_files(output_dir)

page_paths = output_dir.glob('qualtran/**/*.md')
page_paths = output_dir.glob('**/*.md')
for path in page_paths:
if fixup_all_symbols_page(path):
continue
Expand Down
152 changes: 152 additions & 0 deletions dev_tools/qualtran_dev_tools/tensor_report_card.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
# Copyright 2024 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import multiprocessing.connection
import time
from typing import Any, Callable, Dict, List, Optional, Tuple

from attrs import define

from qualtran import Bloq
from qualtran.simulation.tensor import cbloq_to_quimb


@define
class _Pending:
"""Helper dataclass to track currently executing processes in `ExecuteWithTimeout`."""

p: multiprocessing.Process
recv: multiprocessing.connection.Connection
start_time: float
kwargs: Dict[str, Any]


class ExecuteWithTimeout:
"""Execute tasks in processes where each task will be killed if it exceeds `timeout`.
Seemingly all the existing "timeout" parameters in the various built-in concurrency
primitives in Python won't actually terminate the process. This one does.
"""

def __init__(self, timeout: float, max_workers: int):
self.timeout = timeout
self.max_workers = max_workers

self.queued: List[Tuple[Callable, Dict[str, Any]]] = []
self.pending: List[_Pending] = []

@property
def work_to_be_done(self) -> int:
"""The number of tasks currently executing or queued."""
return len(self.queued) + len(self.pending)

def submit(self, func: Callable, kwargs: Dict[str, Any]) -> None:
"""Add a task to the queue.
`func` must be a callable that can accept `kwargs` in addition to
a keyword argument `cxn` which is a multiprocessing `Connection` object that forms
the sending-half of a `mp.Pipe`. The callable must call `cxn.send(...)`
to return a result.
"""
self.queued.append((func, kwargs))

def _submit_from_queue(self):
# helper method that takes an item from the queue, launches a process,
# and records it in the `pending` attribute. This must only be called
# if we're allowed to spawn a new process.
func, kwargs = self.queued.pop(0)
recv, send = multiprocessing.Pipe(duplex=False)
kwargs['cxn'] = send
p = multiprocessing.Process(target=func, kwargs=kwargs)
start_time = time.time()
p.start()
self.pending.append(_Pending(p=p, recv=recv, start_time=start_time, kwargs=kwargs))

def _scan_pendings(self) -> Optional[_Pending]:
# helper method that goes through the currently pending tasks, terminates the ones
# that have been going on too long, and accounts for ones that have finished.
# Returns the `_Pending` of the killed or completed job or `None` if each pending
# task is still running but none have exceeded the timeout.
for i in range(len(self.pending)):
pen = self.pending[i]

if not pen.p.is_alive():
self.pending.pop(i)
pen.p.join()
return pen

if time.time() - pen.start_time > self.timeout:
pen.p.terminate()
self.pending.pop(i)
return pen

return None

def next_result(self) -> Tuple[Dict[str, Any], Optional[Any]]:
"""Get the next available result.
This call is blocking, but should never take longer than `self.timeout`. This should
be called in a loop to make sure the queue continues to be processed.
Returns:
task kwargs: The keyword arguments used to submit the task.
result: If the process finished successfully, this is the object that was
sent through the multiprocessing pipe as the result. Otherwise, the result
is None.
"""
while len(self.queued) > 0 and len(self.pending) < self.max_workers:
self._submit_from_queue()

while True:
finished = self._scan_pendings()
if finished is not None:
break

if finished.p.exitcode == 0:
result = finished.recv.recv()
else:
result = None

finished.recv.close()

while len(self.queued) > 0 and len(self.pending) < self.max_workers:
self._submit_from_queue()

return (finished.kwargs, result)


def report_on_tensors(name: str, cls_name: str, bloq: Bloq, cxn) -> None:
"""Get timing information for tensor functionality.
This should be used with `ExecuteWithTimeout`. The resultant
record dictionary is sent over `cxn`.
"""
record: Dict[str, Any] = {'name': name, 'cls': cls_name}

try:
start = time.perf_counter()
flat = bloq.as_composite_bloq().flatten()
record['flat_dur'] = time.perf_counter() - start

start = time.perf_counter()
tn = cbloq_to_quimb(flat)
record['tn_dur'] = time.perf_counter() - start

start = time.perf_counter()
record['width'] = tn.contraction_width()
record['width_dur'] = time.perf_counter() - start

except Exception as e: # pylint: disable=broad-exception-caught
record['err'] = str(e)

cxn.send(record)
2 changes: 1 addition & 1 deletion dev_tools/requirements/deps/format.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
flynt~=0.60
black~=22.3.0
black~=24.8.0
isort~=5.10.1
4 changes: 2 additions & 2 deletions dev_tools/requirements/deps/pylint.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pylint~=2.15
pylint~=3.3.1

# for checking _test.py files
pytest
Expand All @@ -7,4 +7,4 @@ openfermion[resources]
# dev tools
tensorflow-docs
sphinx
filelock
filelock
5 changes: 3 additions & 2 deletions dev_tools/requirements/deps/runtime.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ numpy
sympy
cirq-core==1.4
fxpmath
galois

# qualtran/testing.py
nbconvert
Expand All @@ -30,8 +31,8 @@ qsharp
qsharp-widgets

# qref bartiq interop
qref>=0.7.0
bartiq>=0.5.1
qref==0.7.0
bartiq==0.6.0

# serialization
protobuf
Expand Down
Loading

0 comments on commit 96a46ea

Please sign in to comment.