diff --git a/.github/workflows/on-push.yml b/.github/workflows/on-push.yml
new file mode 100644
index 0000000..ff690f7
--- /dev/null
+++ b/.github/workflows/on-push.yml
@@ -0,0 +1,79 @@
+name: Push workflow
+
+on:
+ push:
+ branches:
+ - '**'
+ tags-ignore:
+ - '**'
+ paths-ignore: # Don't trigger on files that are updated by the CI
+ - 'pyproject.toml' # Note changes to poetry.lock will still build
+ - 'setup.py'
+ - 'README.rst'
+
+jobs:
+
+ housekeeping:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v1
+ - name: Perform housekeeping checks
+ env:
+ GCLOUD_CONTAINER_AUTH: ${{ secrets.GCLOUD_CONTAINER_AUTH }}
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ run: |
+ source <(curl -sL http://ci.q-ctrl.com)
+ ./ci vault login -r ${{ secrets.VAULT_ROLE_ID }} -s ${{ secrets.VAULT_SECRET_ID }}
+ ./ci docker run asia.gcr.io/q-ctrl-api-development/python-37 /scripts/housekeeping.sh
+
+ linting:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v1
+ - name: Install Python dependencies
+ env:
+ GCLOUD_CONTAINER_AUTH: ${{ secrets.GCLOUD_CONTAINER_AUTH }}
+ run: |
+ source <(curl -sL http://ci.q-ctrl.com)
+ ./ci vault login -r ${{ secrets.VAULT_ROLE_ID }} -s ${{ secrets.VAULT_SECRET_ID }}
+ ./ci docker run asia.gcr.io/q-ctrl-api-development/python-37 /scripts/install-python-dependencies.sh
+ - name: Run Pylint
+ run: |
+ ./ci docker run asia.gcr.io/q-ctrl-api-development/python-37 poetry run pylint_runner || true
+ - name: Run Pylama
+ run: |
+ ./ci docker run asia.gcr.io/q-ctrl-api-development/python-37 poetry run pylama || true
+ - name: Run Markdownlint
+ run: |
+ ./ci docker run asia.gcr.io/q-ctrl-api-development/python-37 mdl -- -ig . || true
+
+ pytest:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ python: [36, 37]
+ steps:
+ - uses: actions/checkout@v1
+ - name: Install Python dependencies
+ env:
+ GCLOUD_CONTAINER_AUTH: ${{ secrets.GCLOUD_CONTAINER_AUTH }}
+ run: |
+ source <(curl -sL http://ci.q-ctrl.com)
+ ./ci vault login -r ${{ secrets.VAULT_ROLE_ID }} -s ${{ secrets.VAULT_SECRET_ID }}
+ ./ci docker run asia.gcr.io/q-ctrl-api-development/python-${{ matrix.python }} /scripts/install-python-dependencies.sh
+ - name: Run Pytest
+ run: |
+ ./ci docker run asia.gcr.io/q-ctrl-api-development/python-${{ matrix.python }} /scripts/pytest.sh
+
+ publish_internally:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v1
+ - name: Publish development version
+ env:
+ GCLOUD_CONTAINER_AUTH: ${{ secrets.GCLOUD_CONTAINER_AUTH }}
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ run: |
+ source <(curl -sL http://ci.q-ctrl.com)
+ ./ci vault login -r ${{ secrets.VAULT_ROLE_ID }} -s ${{ secrets.VAULT_SECRET_ID }}
+ ./ci docker run asia.gcr.io/q-ctrl-api-development/python-37 /scripts/publish-dev-version.sh
\ No newline at end of file
diff --git a/.github/workflows/on-release.yml b/.github/workflows/on-release.yml
new file mode 100644
index 0000000..7b0ae50
--- /dev/null
+++ b/.github/workflows/on-release.yml
@@ -0,0 +1,32 @@
+name: Release workflow
+
+on:
+ release:
+ types: [published]
+
+jobs:
+
+ release:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v1
+ - name: Update version in code
+ env:
+ GCLOUD_CONTAINER_AUTH: ${{ secrets.GCLOUD_CONTAINER_AUTH }}
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ run: |
+ source <(curl -sL http://ci.q-ctrl.com)
+ ./ci vault login -r ${{ secrets.VAULT_ROLE_ID }} -s ${{ secrets.VAULT_SECRET_ID }}
+ ./ci docker run asia.gcr.io/q-ctrl-api-development/python-37 /scripts/housekeeping.sh
+ - name: Publish publicly
+ env:
+ GCLOUD_CONTAINER_AUTH: ${{ secrets.GCLOUD_CONTAINER_AUTH }}
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ run: |
+ source <(curl -sL http://ci.q-ctrl.com)
+ ./ci docker run asia.gcr.io/q-ctrl-api-development/python-37 /scripts/publish-release-publicly.sh
+ - name: Publish internally
+ env:
+ GCLOUD_CONTAINER_AUTH: ${{ secrets.GCLOUD_CONTAINER_AUTH }}
+ run: |
+ ./ci docker run asia.gcr.io/q-ctrl-api-development/python-37 /scripts/publish-release-internally.sh
\ No newline at end of file
diff --git a/.pylintrc b/.pylintrc
new file mode 100644
index 0000000..f8ebdde
--- /dev/null
+++ b/.pylintrc
@@ -0,0 +1,545 @@
+[MASTER]
+
+# A comma-separated list of package or module names from where C extensions may
+# be loaded. Extensions are loading into the active Python interpreter and may
+# run arbitrary code
+extension-pkg-whitelist=
+
+# Add files or directories to the blacklist. They should be base names, not
+# paths.
+ignore=CVS
+
+# Add files or directories matching the regex patterns to the blacklist. The
+# regex matches against base names, not paths.
+ignore-patterns=
+
+# Python code to execute, usually for sys.path manipulation such as
+# pygtk.require().
+#init-hook=
+
+# Use multiple processes to speed up Pylint.
+jobs=1
+
+# List of plugins (as comma separated values of python modules names) to load,
+# usually to register additional checkers.
+load-plugins=pylint.extensions.docparams
+
+# Pickle collected data for later comparisons.
+persistent=yes
+
+# Specify a configuration file.
+#rcfile=
+
+# When enabled, pylint would attempt to guess common misconfiguration and emit
+# user-friendly hints instead of false-positive error messages
+suggestion-mode=yes
+
+# Allow loading of arbitrary C extensions. Extensions are imported into the
+# active Python interpreter and may run arbitrary code.
+unsafe-load-any-extension=no
+
+
+[MESSAGES CONTROL]
+
+# Only show warnings with the listed confidence levels. Leave empty to show
+# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED
+confidence=
+
+# Disable the message, report, category or checker with the given id(s). You
+# can either give multiple identifiers separated by comma (,) or put this
+# option multiple times (only on the command line, not in the configuration
+# file where it should appear only once).You can also use "--disable=all" to
+# disable everything first and then reenable specific checks. For example, if
+# you want to run only the similarities checker, you can use "--disable=all
+# --enable=similarities". If you want to run only the classes checker, but have
+# no Warning level messages displayed, use"--disable=all --enable=classes
+# --disable=W"
+disable=duplicate-code,
+ print-statement,
+ parameter-unpacking,
+ unpacking-in-except,
+ old-raise-syntax,
+ backtick,
+ long-suffix,
+ old-ne-operator,
+ old-octal-literal,
+ import-star-module-level,
+ non-ascii-bytes-literal,
+ raw-checker-failed,
+ bad-inline-option,
+ locally-disabled,
+ locally-enabled,
+ file-ignored,
+ suppressed-message,
+ useless-suppression,
+ deprecated-pragma,
+ apply-builtin,
+ basestring-builtin,
+ buffer-builtin,
+ cmp-builtin,
+ coerce-builtin,
+ execfile-builtin,
+ file-builtin,
+ long-builtin,
+ raw_input-builtin,
+ reduce-builtin,
+ standarderror-builtin,
+ unicode-builtin,
+ xran
+ ge-builtin,
+ coerce-method,
+ delslice-method,
+ getslice-method,
+ setslice-method,
+ no-absolute-import,
+ old-division,
+ dict-iter-method,
+ dict-view-method,
+ next-method-called,
+ metaclass-assignment,
+ indexing-exception,
+ raising-string,
+ reload-builtin,
+ oct-method,
+ hex-method,
+ nonzero-method,
+ cmp-method,
+ input-builtin,
+ round-builtin,
+ intern-builtin,
+ unichr-builtin,
+ map-builtin-not-iterating,
+ zip-builtin-not-iterating,
+ range-builtin-not-iterating,
+ filter-builtin-not-iterating,
+ using-cmp-argument,
+ eq-without-hash,
+ div-method,
+ idiv-method,
+ rdiv-method,
+ exception-message-attribute,
+ invalid-str-codec,
+ sys-max-int,
+ bad-python3-import,
+ deprecated-string-function,
+ deprecated-str-translate-call,
+ deprecated-itertools-function,
+ deprecated-types-field,
+ next-method-defined,
+ dict-items-not-iterating,
+ dict-keys-not-iterating,
+ dict-values-not-iterating,
+ useless-object-inheritance
+
+# Enable the message, report, category or checker with the given id(s). You can
+# either give multiple identifier separated by comma (,) or put this option
+# multiple time (only on the command line, not in the configuration file where
+# it should appear only once). See also the "--disable" option for examples.
+enable=c-extension-no-member
+
+
+[REPORTS]
+
+# Python expression which should return a note less than 10 (10 is the highest
+# note). You have access to the variables errors warning, statement which
+# respectively contain the number of errors / warnings messages and the total
+# number of statements analyzed. This is used by the global evaluation report
+# (RP0004).
+evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
+
+# Template used to display messages. This is a python new-style format string
+# used to format the message information. See doc for all details
+#msg-template=
+
+# Set the output format. Available formats are text, parseable, colorized, json
+# and msvs (visual studio).You can also give a reporter class, eg
+# mypackage.mymodule.MyReporterClass.
+output-format=text
+
+# Tells whether to display a full report or only the messages
+reports=no
+
+# Activate the evaluation score.
+score=yes
+
+
+[REFACTORING]
+
+# Maximum number of nested blocks for function / method body
+max-nested-blocks=5
+
+# Complete name of functions that never returns. When checking for
+# inconsistent-return-statements if a never returning function is called then
+# it will be considered as an explicit return statement and no message will be
+# printed.
+never-returning-functions=optparse.Values,sys.exit
+
+
+[LOGGING]
+
+# Logging modules to check that the string format arguments are in logging
+# function parameter format
+logging-modules=logging
+
+
+[SPELLING]
+
+# Limits count of emitted suggestions for spelling mistakes
+max-spelling-suggestions=4
+
+# Spelling dictionary name. Available dictionaries: none. To make it working
+# install python-enchant package.
+spelling-dict=
+
+# List of comma separated words that should not be checked.
+spelling-ignore-words=
+
+# A path to a file that contains private dictionary; one word per line.
+spelling-private-dict-file=
+
+# Tells whether to store unknown words to indicated private dictionary in
+# --spelling-private-dict-file option instead of raising a message.
+spelling-store-unknown-words=no
+
+
+[MISCELLANEOUS]
+
+# List of note tags to take in consideration, separated by a comma.
+notes=FIXME,
+ XXX,
+ TODO
+
+
+[SIMILARITIES]
+
+# Ignore comments when computing similarities.
+ignore-comments=yes
+
+# Ignore docstrings when computing similarities.
+ignore-docstrings=yes
+
+# Ignore imports when computing similarities.
+ignore-imports=no
+
+# Minimum lines number of a similarity.
+min-similarity-lines=4
+
+
+[TYPECHECK]
+
+# List of decorators that produce context managers, such as
+# contextlib.contextmanager. Add to this list to register other decorators that
+# produce valid context managers.
+contextmanager-decorators=contextlib.contextmanager
+
+# List of members which are set dynamically and missed by pylint inference
+# system, and so shouldn't trigger E1101 when accessed. Python regular
+# expressions are accepted.
+generated-members=
+
+# Tells whether missing members accessed in mixin class should be ignored. A
+# mixin class is detected if its name ends with "mixin" (case insensitive).
+ignore-mixin-members=yes
+
+# This flag controls whether pylint should warn about no-member and similar
+# checks whenever an opaque object is returned when inferring. The inference
+# can return multiple potential results while evaluating a Python object, but
+# some branches might not be evaluated, which results in partial inference. In
+# that case, it might be useful to still emit no-member and other checks for
+# the rest of the inferred objects.
+ignore-on-opaque-inference=yes
+
+# List of class names for which member attributes should not be checked (useful
+# for classes with dynamically set attributes). This supports the use of
+# qualified names.
+ignored-classes=optparse.Values,thread._local,_thread._local
+
+# List of module names for which member attributes should not be checked
+# (useful for modules/projects where namespaces are manipulated during runtime
+# and thus existing member attributes cannot be deduced by static analysis. It
+# supports qualified module names, as well as Unix pattern matching.
+ignored-modules=scipy.special
+
+# Show a hint with possible names when a member name was not found. The aspect
+# of finding the hint is based on edit distance.
+missing-member-hint=yes
+
+# The minimum edit distance a name should have in order to be considered a
+# similar match for a missing member name.
+missing-member-hint-distance=1
+
+# The total number of similar names that should be taken in consideration when
+# showing a hint for a missing member.
+missing-member-max-choices=1
+
+
+[VARIABLES]
+
+# List of additional names supposed to be defined in builtins. Remember that
+# you should avoid to define new builtins when possible.
+additional-builtins=
+
+# Tells whether unused global variables should be treated as a violation.
+allow-global-unused-variables=yes
+
+# List of strings which can identify a callback function by name. A callback
+# name must start or end with one of those strings.
+callbacks=cb_,
+ _cb
+
+# A regular expression matching the name of dummy variables (i.e. expectedly
+# not used).
+dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_
+
+# Argument names that match this expression will be ignored. Default to name
+# with leading underscore
+ignored-argument-names=_.*|^ignored_|^unused_
+
+# Tells whether we should check for unused import in __init__ files.
+init-import=no
+
+# List of qualified module names which can have objects that can redefine
+# builtins.
+redefining-builtins-modules=six.moves,past.builtins,future.builtins
+
+
+[FORMAT]
+
+# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
+expected-line-ending-format=
+
+# Regexp for a line that is allowed to be longer than the limit.
+ignore-long-lines=^\s*(# )??$
+
+# Number of spaces of indent required inside a hanging or continued line.
+indent-after-paren=4
+
+# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
+# tab).
+indent-string=' '
+
+# Maximum number of characters on a single line.
+max-line-length=100
+
+# Maximum number of lines in a module
+max-module-lines=1000
+
+# List of optional constructs for which whitespace checking is disabled. `dict-
+# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}.
+# `trailing-comma` allows a space between comma and closing bracket: (a, ).
+# `empty-line` allows space-only lines.
+no-space-check=trailing-comma,
+ dict-separator
+
+# Allow the body of a class to be on the same line as the declaration if body
+# contains single statement.
+single-line-class-stmt=no
+
+# Allow the body of an if to be on the same line as the test if there is no
+# else.
+single-line-if-stmt=no
+
+
+[BASIC]
+
+# Naming style matching correct argument names
+argument-naming-style=snake_case
+
+# Regular expression matching correct argument names. Overrides argument-
+# naming-style
+#argument-rgx=
+
+# Naming style matching correct attribute names
+attr-naming-style=snake_case
+
+# Regular expression matching correct attribute names. Overrides attr-naming-
+# style
+#attr-rgx=
+
+# Bad variable names which should always be refused, separated by a comma
+bad-names=foo,
+ bar,
+ baz,
+ toto,
+ tutu,
+ tata
+
+# Naming style matching correct class attribute names
+class-attribute-naming-style=any
+
+# Regular expression matching correct class attribute names. Overrides class-
+# attribute-naming-style
+#class-attribute-rgx=
+
+# Naming style matching correct class names
+class-naming-style=PascalCase
+
+# Regular expression matching correct class names. Overrides class-naming-style
+#class-rgx=
+
+# Naming style matching correct constant names
+const-naming-style=UPPER_CASE
+
+# Regular expression matching correct constant names. Overrides const-naming-
+# style
+#const-rgx=
+
+# Minimum line length for functions/classes that require docstrings, shorter
+# ones are exempt.
+docstring-min-length=20
+
+# Naming style matching correct function names
+function-naming-style=snake_case
+
+# Regular expression matching correct function names. Overrides function-
+# naming-style
+#function-rgx=
+
+# Good variable names which should always be accepted, separated by a comma
+good-names=i,
+ j,
+ k,
+ ex,
+ Run,
+ _
+
+# Include a hint for the correct naming format with invalid-name
+include-naming-hint=no
+
+# Naming style matching correct inline iteration names
+inlinevar-naming-style=any
+
+# Regular expression matching correct inline iteration names. Overrides
+# inlinevar-naming-style
+#inlinevar-rgx=
+
+# Naming style matching correct method names
+method-naming-style=snake_case
+
+# Regular expression matching correct method names. Overrides method-naming-
+# style
+#method-rgx=
+
+# Naming style matching correct module names
+module-naming-style=snake_case
+
+# Regular expression matching correct module names. Overrides module-naming-
+# style
+#module-rgx=
+
+# Colon-delimited sets of names that determine each other's naming style when
+# the name regexes allow several styles.
+name-group=
+
+# Regular expression which should only match function or class names that do
+# not require a docstring.
+no-docstring-rgx=^_
+
+# List of decorators that produce properties, such as abc.abstractproperty. Add
+# to this list to register other decorators that produce valid properties.
+property-classes=abc.abstractproperty
+
+# Naming style matching correct variable names
+variable-naming-style=snake_case
+
+# Regular expression matching correct variable names. Overrides variable-
+# naming-style
+#variable-rgx=
+
+
+[DESIGN]
+
+# Maximum number of arguments for function / method
+max-args=16
+
+# Maximum number of attributes for a class (see R0902).
+max-attributes=64
+
+# Maximum number of boolean expressions in a if statement
+max-bool-expr=8
+
+# Maximum number of branch for function / method body
+max-branches=32
+
+# Maximum number of locals for function / method body
+max-locals=32
+
+# Maximum number of parents for a class (see R0901).
+max-parents=16
+
+# Maximum number of public methods for a class (see R0904).
+max-public-methods=64
+
+# Maximum number of return / yield for function / method body
+max-returns=8
+
+# Maximum number of statements in function / method body
+max-statements=128
+
+# Minimum number of public methods for a class (see R0903).
+min-public-methods=1
+
+
+[CLASSES]
+
+# List of method names used to declare (i.e. assign) instance attributes.
+defining-attr-methods=__init__,
+ __new__,
+ setUp
+
+# List of member names, which should be excluded from the protected access
+# warning.
+exclude-protected=_asdict,
+ _fields,
+ _replace,
+ _source,
+ _make
+
+# List of valid names for the first argument in a class method.
+valid-classmethod-first-arg=cls
+
+# List of valid names for the first argument in a metaclass class method.
+valid-metaclass-classmethod-first-arg=mcs
+
+
+[IMPORTS]
+
+# Allow wildcard imports from modules that define __all__.
+allow-wildcard-with-all=no
+
+# Analyse import fallback blocks. This can be used to support both Python 2 and
+# 3 compatible code, which means that the block might have code that exists
+# only in one or another interpreter, leading to false positives when analysed.
+analyse-fallback-blocks=no
+
+# Deprecated modules which should not be used, separated by a comma
+deprecated-modules=regsub,
+ TERMIOS,
+ Bastion,
+ rexec
+
+# Create a graph of external dependencies in the given file (report RP0402 must
+# not be disabled)
+ext-import-graph=
+
+# Create a graph of every (i.e. internal and external) dependencies in the
+# given file (report RP0402 must not be disabled)
+import-graph=
+
+# Create a graph of internal dependencies in the given file (report RP0402 must
+# not be disabled)
+int-import-graph=
+
+# Force import order to recognize a module as part of the standard
+# compatibility libraries.
+known-standard-library=
+
+# Force import order to recognize a module as part of a third party library.
+known-third-party=enchant
+
+
+[EXCEPTIONS]
+
+# Exceptions that will emit a warning when being caught. Defaults to
+# "Exception"
+overgeneral-exceptions=Exception
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..261eeb9
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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
+
+ http://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.
diff --git a/README.md b/README.md
index 7cf3c9e..6ac07da 100644
--- a/README.md
+++ b/README.md
@@ -1,13 +1,12 @@
# Q-CTRL Cirq Adaptor
-The aim of the Q-CTRL Cirq Adapter package is to provide export functions allowing
+The aim of the Q-CTRL Cirq Adaptor package is to provide export functions allowing
users to deploy established error-robust quantum control protocols from the
-open literature and defined in Q-CTRL Open Controls on on Google Quantum devices
+open literature and defined in Q-CTRL Open Controls on Google quantum devices
and simulators.
Anyone interested in quantum control is welcome to contribute to this project.
-
## Table of Contents
- [Installation](#installation)
diff --git a/README.rst b/README.rst
new file mode 100644
index 0000000..f6e2e7f
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,150 @@
+
+Q-CTRL Cirq Adaptor
+===================
+
+The aim of the Q-CTRL Cirq Adaptor package is to provide export functions allowing
+users to deploy established error-robust quantum control protocols from the
+open literature and defined in Q-CTRL Open Controls on Google quantum devices
+and simulators.
+
+Anyone interested in quantum control is welcome to contribute to this project.
+
+Table of Contents
+-----------------
+
+
+* `Installation <#installation>`_
+* `Usage <#usage>`_
+* `Contributing <#contributing>`_
+* `Credits <#credits>`_
+* `License <#license>`_
+
+Installation
+------------
+
+Q-CTRL Cirq Adaptor can be installed through ``pip`` or from source. We recommend
+the ``pip`` distribution to get the most recent stable release. If you want the
+latest features then install from source.
+
+Requirements
+^^^^^^^^^^^^
+
+To use Q-CTRL Cirq Adaptor you will need an installation of Python. We
+recommend using the `Anaconda `_ distribution of
+Python. Anaconda includes standard numerical and scientific Python packages
+which are optimally compiled for your machine. Follow the `Anaconda
+Installation `_ instructions and
+consult the `Anaconda User
+guide `_ to get started.
+
+We use interactive jupyter notebooks for our usage examples. The Anaconda
+python distribution comes with editors for these files, or you can `install the
+jupyter notebook editor `_ on its own.
+
+Using PyPi
+^^^^^^^^^^
+
+Use ``pip`` to install the latest version of Q-CTRL Cirq Adaptor.
+
+.. code-block:: shell
+
+ pip install qctrl-cirq
+
+From Source
+^^^^^^^^^^^
+
+The source code is hosted on
+`Github `_. The repository can be
+cloned using
+
+.. code-block:: shell
+
+ git clone git@github.com:qctrl/python-cirq.git
+
+Once the clone is complete, you have two options:
+
+
+#.
+ Using setup.py
+
+ .. code-block:: shell
+
+ cd python-cirq
+ python setup.py develop
+
+ **Note:** We recommend installing using ``develop`` to point your installation
+ at the source code in the directory where you cloned the repository.
+
+#.
+ Using Poetry
+
+ .. code-block:: shell
+
+ cd python-cirq
+ ./setup-poetry.sh
+
+ **Note:** if you are on Windows, you'll need to install
+ `Poetry `_ manually, and use:
+
+ .. code-block:: bash
+
+ cd python-cirq
+ poetry install
+
+Once installed via one of the above methods, test your installation by running
+``pytest``
+in the ``python-cirq`` directory.
+
+.. code-block:: shell
+
+ pytest
+
+Usage
+-----
+
+See the `Jupyter notebooks `_.
+
+Contributing
+------------
+
+For general guidelines, see `Contributing `_.
+
+Building documentation
+^^^^^^^^^^^^^^^^^^^^^^
+
+Documentation generation relies on `Spinx `_. Automated builds are done by `Read The Docs `_.
+
+To build locally:
+
+
+#. Ensure you have used one of the install options above.
+#.
+ Execute the make file from the docs directory:
+
+ If using Poetry:
+
+ .. code-block:: bash
+
+ cd docs
+ poetry run make html
+
+ If using setuptools:
+
+ .. code-block:: bash
+
+ cd docs
+ # Activate your virtual environment if required
+ make html
+
+The generated HTML will appear in the ``docs/_build/html`` directory.
+
+Credits
+-------
+
+See
+`Contributors `_.
+
+License
+-------
+
+See `LICENSE `_.
diff --git a/docs/Makefile b/docs/Makefile
new file mode 100644
index 0000000..d4bb2cb
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1,20 @@
+# Minimal makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line, and also
+# from the environment for the first two.
+SPHINXOPTS ?=
+SPHINXBUILD ?= sphinx-build
+SOURCEDIR = .
+BUILDDIR = _build
+
+# Put it first so that "make" without argument is like "make help".
+help:
+ @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+
+.PHONY: help Makefile
+
+# Catch-all target: route all unknown targets to Sphinx using the new
+# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
+%: Makefile
+ @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
diff --git a/docs/_static/css/custom.css b/docs/_static/css/custom.css
new file mode 100644
index 0000000..1e8588d
--- /dev/null
+++ b/docs/_static/css/custom.css
@@ -0,0 +1,3 @@
+/* .wy-side-nav-search {
+ background-color: #2980B9;
+} */
\ No newline at end of file
diff --git a/docs/_templates/breadcrumbs.html b/docs/_templates/breadcrumbs.html
new file mode 100644
index 0000000..339f008
--- /dev/null
+++ b/docs/_templates/breadcrumbs.html
@@ -0,0 +1,4 @@
+{%- extends "sphinx_rtd_theme/breadcrumbs.html" %}
+
+{% block breadcrumbs_aside %}
+{% endblock %}
\ No newline at end of file
diff --git a/docs/conf.py b/docs/conf.py
new file mode 100644
index 0000000..3da3537
--- /dev/null
+++ b/docs/conf.py
@@ -0,0 +1,63 @@
+# Configuration file for the Sphinx documentation builder.
+#
+# This file only contains a selection of the most common options. For a full
+# list see the documentation:
+# https://www.sphinx-doc.org/en/master/usage/configuration.html
+
+# -- Path setup --------------------------------------------------------------
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#
+import os
+import sys
+import toml
+
+sys.path.insert(0, os.path.abspath('..'))
+
+# -- Project information -----------------------------------------------------
+parsed = toml.load("../pyproject.toml")
+package_info = parsed['tool']['poetry']
+project = package_info['description']
+author = ", ".join(package_info['authors'])
+release = package_info['version']
+copyright = '2019, Q-CTRL '
+
+# -- General configuration ---------------------------------------------------
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = [
+ 'sphinx.ext.autodoc',
+ 'sphinx.ext.coverage',
+ 'sphinx.ext.mathjax',
+ 'sphinx.ext.napoleon'
+]
+
+master_doc = 'index'
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+# This pattern also affects html_static_path and html_extra_path.
+exclude_patterns = []
+
+
+# -- Options for HTML output -------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+#
+html_theme = 'default'
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+html_css_files = [
+ 'css/custom.css',
+]
diff --git a/docs/index.rst b/docs/index.rst
new file mode 100644
index 0000000..2085478
--- /dev/null
+++ b/docs/index.rst
@@ -0,0 +1,115 @@
+
+Q-CTRL Cirq Adaptor
+===================
+
+Aim of the Q-CTRL Cirq Adaptor package is to provide easy to use export functions
+allowing users to deploy the quantum controls techniques, define in Q-CTRL Open
+Controls, on Google quantum devices and simulators.
+
+Installation
+------------
+
+Q-CTRL Cirq Adaptor can be install through ``pip`` or from source. We recommend
+the ``pip`` distribution to get the most recent stable release. If you want the
+latest features then install from source.
+
+Requirements
+^^^^^^^^^^^^
+
+To use Q-CTRL Cirq Adaptor you will need an installation of Python. We
+recommend using the `Anaconda `_ distribution of
+Python. Anaconda includes standard numerical and scientific Python packages
+which are optimally compiled for your machine. Follow the `Anaconda
+Installation `_ instructions and
+consult the `Anaconda User
+guide `_ to get started.
+
+We use interactive jupyter notebooks for our usage examples. The Anaconda
+python distribution comes with editors for these files, or you can `install the
+jupyter notebook editor `_ on its own.
+
+Using PyPi
+^^^^^^^^^^
+
+Use ``pip`` to install the latest version of Q-CTRL Cirq Adaptor.
+
+.. code-block:: shell
+
+ pip install qctrl-cirq
+
+From Source
+^^^^^^^^^^^
+
+The source code is hosted on
+`Github `_. The repository can be
+cloned using
+
+.. code-block:: shell
+
+ git clone git@github.com:qctrl/python-cirq.git
+
+Once the clone is complete, you have two options:
+
+
+#.
+ Using setup.py
+
+ .. code-block:: shell
+
+ cd python-cirq
+ python setup.py develop
+
+ **Note:** We recommend installing using ``develop`` to point your installation
+ at the source code in the directory where you cloned the repository.
+
+#.
+ Using Poetry
+
+ .. code-block:: shell
+
+ cd python-cirq
+ ./setup-poetry.sh
+
+ **Note:** if you are on Windows, you'll need to install
+ `Poetry `_ manually, and use:
+
+ .. code-block:: shell
+
+ cd python-cirq
+ poetry install
+
+Once installed via one of the above methods, test your installation by running
+``pytest``
+in the ``python-cirq`` directory.
+
+.. code-block:: shell
+
+ pytest
+
+Usage
+-----
+
+See the `Jupyter notebooks `_.
+
+Reference
+---------
+
+.. toctree::
+ :maxdepth: 2
+ :caption: Contents:
+
+ qctrlcirq
+
+Licence
+-------
+
+.. toctree::
+
+ licence
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
diff --git a/docs/licence.rst b/docs/licence.rst
new file mode 100644
index 0000000..7e5f93c
--- /dev/null
+++ b/docs/licence.rst
@@ -0,0 +1,16 @@
+Licence
+=======
+
+Copyright 2019 Q-CTRL
+
+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
+
+ http://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.
\ No newline at end of file
diff --git a/docs/matplotlibrc b/docs/matplotlibrc
new file mode 100644
index 0000000..ee3279e
--- /dev/null
+++ b/docs/matplotlibrc
@@ -0,0 +1 @@
+backend: agg
\ No newline at end of file
diff --git a/docs/qctrlcirq.rst b/docs/qctrlcirq.rst
new file mode 100644
index 0000000..e84e8fb
--- /dev/null
+++ b/docs/qctrlcirq.rst
@@ -0,0 +1,21 @@
+qctrlcirq package
+===================
+
+Submodules
+----------
+
+qctrlcirq.circuit module
+------------------------
+
+.. automodule:: qctrlcirq.circuit
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+qctrlcirq.schedule module
+-------------------------
+
+.. automodule:: qctrlcirq.schedule
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/matplotlibrc b/matplotlibrc
new file mode 100644
index 0000000..ee3279e
--- /dev/null
+++ b/matplotlibrc
@@ -0,0 +1 @@
+backend: agg
\ No newline at end of file
diff --git a/poetry.lock b/poetry.lock
new file mode 100644
index 0000000..36c7b34
--- /dev/null
+++ b/poetry.lock
@@ -0,0 +1,834 @@
+[[package]]
+category = "dev"
+description = "A configurable sidebar-enabled Sphinx theme"
+name = "alabaster"
+optional = false
+python-versions = "*"
+version = "0.7.12"
+
+[[package]]
+category = "dev"
+description = "An abstract syntax tree for Python with inference support."
+name = "astroid"
+optional = false
+python-versions = ">=3.5.*"
+version = "2.3.3"
+
+[package.dependencies]
+lazy-object-proxy = ">=1.4.0,<1.5.0"
+six = ">=1.12,<2.0"
+wrapt = ">=1.11.0,<1.12.0"
+
+[package.dependencies.typed-ast]
+python = "<3.8"
+version = ">=1.4.0,<1.5"
+
+[[package]]
+category = "dev"
+description = "Atomic file writes."
+name = "atomicwrites"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+version = "1.3.0"
+
+[[package]]
+category = "dev"
+description = "Classes Without Boilerplate"
+name = "attrs"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+version = "19.3.0"
+
+[[package]]
+category = "dev"
+description = "Internationalization utilities"
+name = "babel"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+version = "2.7.0"
+
+[package.dependencies]
+pytz = ">=2015.7"
+
+[[package]]
+category = "main"
+description = "Extensible memoizing collections and decorators"
+name = "cachetools"
+optional = false
+python-versions = "*"
+version = "3.1.1"
+
+[[package]]
+category = "main"
+description = "Python package for providing Mozilla's CA Bundle."
+name = "certifi"
+optional = false
+python-versions = "*"
+version = "2019.9.11"
+
+[[package]]
+category = "main"
+description = "Universal encoding detector for Python 2 and 3"
+name = "chardet"
+optional = false
+python-versions = "*"
+version = "3.0.4"
+
+[[package]]
+category = "main"
+description = "A framework for creating, editing, and invoking Noisy Intermediate Scale Quantum (NISQ) circuits."
+name = "cirq"
+optional = false
+python-versions = ">=3.6.0"
+version = "0.6.0"
+
+[package.dependencies]
+google-api-python-client = ">=1.6,<2.0"
+matplotlib = ">=3.0,<4.0"
+networkx = "2.3"
+numpy = ">=1.16,<2.0"
+pandas = "*"
+protobuf = "3.8.0"
+requests = ">=2.18,<3.0"
+scipy = "*"
+sortedcontainers = ">=2.0,<3.0"
+sympy = "*"
+typing-extensions = "*"
+
+[package.dependencies.dataclasses]
+python = "<3.7"
+version = "*"
+
+[[package]]
+category = "dev"
+description = "Cross-platform colored terminal text."
+name = "colorama"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+version = "0.4.1"
+
+[[package]]
+category = "main"
+description = "Composable style cycles"
+name = "cycler"
+optional = false
+python-versions = "*"
+version = "0.10.0"
+
+[package.dependencies]
+six = "*"
+
+[[package]]
+category = "main"
+description = "A backport of the dataclasses module for Python 3.6"
+marker = "python_version < \"3.7\""
+name = "dataclasses"
+optional = false
+python-versions = "*"
+version = "0.6"
+
+[[package]]
+category = "main"
+description = "Decorators for Humans"
+name = "decorator"
+optional = false
+python-versions = ">=2.6, !=3.0.*, !=3.1.*"
+version = "4.4.1"
+
+[[package]]
+category = "dev"
+description = "Docutils -- Python Documentation Utilities"
+name = "docutils"
+optional = false
+python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
+version = "0.15.2"
+
+[[package]]
+category = "main"
+description = "Google API Client Library for Python"
+name = "google-api-python-client"
+optional = false
+python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*"
+version = "1.7.11"
+
+[package.dependencies]
+google-auth = ">=1.4.1"
+google-auth-httplib2 = ">=0.0.3"
+httplib2 = ">=0.9.2,<1dev"
+six = ">=1.6.1,<2dev"
+uritemplate = ">=3.0.0,<4dev"
+
+[[package]]
+category = "main"
+description = "Google Authentication Library"
+name = "google-auth"
+optional = false
+python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*"
+version = "1.7.0"
+
+[package.dependencies]
+cachetools = ">=2.0.0,<3.2"
+pyasn1-modules = ">=0.2.1"
+rsa = ">=3.1.4,<4.1"
+setuptools = ">=40.3.0"
+six = ">=1.9.0"
+
+[[package]]
+category = "main"
+description = "Google Authentication Library: httplib2 transport"
+name = "google-auth-httplib2"
+optional = false
+python-versions = "*"
+version = "0.0.3"
+
+[package.dependencies]
+google-auth = "*"
+httplib2 = ">=0.9.1"
+
+[[package]]
+category = "main"
+description = "A comprehensive HTTP client library."
+name = "httplib2"
+optional = false
+python-versions = "*"
+version = "0.14.0"
+
+[[package]]
+category = "main"
+description = "Internationalized Domain Names in Applications (IDNA)"
+name = "idna"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+version = "2.8"
+
+[[package]]
+category = "dev"
+description = "Getting image size from png/jpeg/jpeg2000/gif file"
+name = "imagesize"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+version = "1.1.0"
+
+[[package]]
+category = "dev"
+description = "Read metadata from Python packages"
+marker = "python_version < \"3.8\""
+name = "importlib-metadata"
+optional = false
+python-versions = ">=2.7,!=3.0,!=3.1,!=3.2,!=3.3"
+version = "0.23"
+
+[package.dependencies]
+zipp = ">=0.5"
+
+[[package]]
+category = "dev"
+description = "A Python utility / library to sort Python imports."
+name = "isort"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+version = "4.3.21"
+
+[[package]]
+category = "dev"
+description = "A very fast and expressive template engine."
+name = "jinja2"
+optional = false
+python-versions = "*"
+version = "2.10.3"
+
+[package.dependencies]
+MarkupSafe = ">=0.23"
+
+[[package]]
+category = "main"
+description = "A fast implementation of the Cassowary constraint solver"
+name = "kiwisolver"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+version = "1.1.0"
+
+[package.dependencies]
+setuptools = "*"
+
+[[package]]
+category = "dev"
+description = "A fast and thorough lazy object proxy."
+name = "lazy-object-proxy"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+version = "1.4.3"
+
+[[package]]
+category = "dev"
+description = "Safely add untrusted strings to HTML/XML markup."
+name = "markupsafe"
+optional = false
+python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*"
+version = "1.1.1"
+
+[[package]]
+category = "main"
+description = "Python plotting package"
+name = "matplotlib"
+optional = false
+python-versions = ">=3.6"
+version = "3.1.1"
+
+[package.dependencies]
+cycler = ">=0.10"
+kiwisolver = ">=1.0.1"
+numpy = ">=1.11"
+pyparsing = ">=2.0.1,<2.0.4 || >2.0.4,<2.1.2 || >2.1.2,<2.1.6 || >2.1.6"
+python-dateutil = ">=2.1"
+
+[[package]]
+category = "dev"
+description = "McCabe checker, plugin for flake8"
+name = "mccabe"
+optional = false
+python-versions = "*"
+version = "0.6.1"
+
+[[package]]
+category = "dev"
+description = "More routines for operating on iterables, beyond itertools"
+name = "more-itertools"
+optional = false
+python-versions = ">=3.4"
+version = "7.2.0"
+
+[[package]]
+category = "main"
+description = "Python library for arbitrary-precision floating-point arithmetic"
+name = "mpmath"
+optional = false
+python-versions = "*"
+version = "1.1.0"
+
+[[package]]
+category = "main"
+description = "Python package for creating and manipulating graphs and networks"
+name = "networkx"
+optional = false
+python-versions = ">=3.5"
+version = "2.3"
+
+[package.dependencies]
+decorator = ">=4.3.0"
+
+[[package]]
+category = "main"
+description = "NumPy is the fundamental package for array computing with Python."
+name = "numpy"
+optional = false
+python-versions = ">=3.5"
+version = "1.17.4"
+
+[[package]]
+category = "dev"
+description = "Core utilities for Python packages"
+name = "packaging"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+version = "19.2"
+
+[package.dependencies]
+pyparsing = ">=2.0.2"
+six = "*"
+
+[[package]]
+category = "main"
+description = "Powerful data structures for data analysis, time series, and statistics"
+name = "pandas"
+optional = false
+python-versions = ">=3.5.3"
+version = "0.25.3"
+
+[package.dependencies]
+numpy = ">=1.13.3"
+python-dateutil = ">=2.6.1"
+pytz = ">=2017.2"
+
+[[package]]
+category = "dev"
+description = "plugin and hook calling mechanisms for python"
+name = "pluggy"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+version = "0.13.0"
+
+[package.dependencies]
+[package.dependencies.importlib-metadata]
+python = "<3.8"
+version = ">=0.12"
+
+[[package]]
+category = "main"
+description = "Protocol Buffers"
+name = "protobuf"
+optional = false
+python-versions = "*"
+version = "3.8.0"
+
+[package.dependencies]
+setuptools = "*"
+six = ">=1.9"
+
+[[package]]
+category = "dev"
+description = "library with cross-python path, ini-parsing, io, code, log facilities"
+name = "py"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+version = "1.8.0"
+
+[[package]]
+category = "main"
+description = "ASN.1 types and codecs"
+name = "pyasn1"
+optional = false
+python-versions = "*"
+version = "0.4.7"
+
+[[package]]
+category = "main"
+description = "A collection of ASN.1-based protocols modules."
+name = "pyasn1-modules"
+optional = false
+python-versions = "*"
+version = "0.2.7"
+
+[package.dependencies]
+pyasn1 = ">=0.4.6,<0.5.0"
+
+[[package]]
+category = "dev"
+description = "Python style guide checker"
+name = "pycodestyle"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+version = "2.5.0"
+
+[[package]]
+category = "dev"
+description = "Python docstring style checker"
+name = "pydocstyle"
+optional = false
+python-versions = ">=3.4"
+version = "4.0.1"
+
+[package.dependencies]
+snowballstemmer = "*"
+
+[[package]]
+category = "dev"
+description = "passive checker of Python programs"
+name = "pyflakes"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+version = "2.1.1"
+
+[[package]]
+category = "dev"
+description = "Pygments is a syntax highlighting package written in Python."
+name = "pygments"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+version = "2.4.2"
+
+[[package]]
+category = "dev"
+description = "pylama -- Code audit tool for python"
+name = "pylama"
+optional = false
+python-versions = "*"
+version = "7.7.1"
+
+[package.dependencies]
+mccabe = ">=0.5.2"
+pycodestyle = ">=2.3.1"
+pydocstyle = ">=2.0.0"
+pyflakes = ">=1.5.0"
+
+[[package]]
+category = "dev"
+description = "python code static checker"
+name = "pylint"
+optional = false
+python-versions = ">=3.5.*"
+version = "2.4.3"
+
+[package.dependencies]
+astroid = ">=2.3.0,<2.4"
+colorama = "*"
+isort = ">=4.2.5,<5"
+mccabe = ">=0.6,<0.7"
+
+[[package]]
+category = "dev"
+description = "Run pylint recursively on all py files in current and sub directories"
+name = "pylint-runner"
+optional = false
+python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*"
+version = "0.5.4"
+
+[package.dependencies]
+colorama = "*"
+pylint = "*"
+
+[[package]]
+category = "main"
+description = "Python parsing module"
+name = "pyparsing"
+optional = false
+python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
+version = "2.4.5"
+
+[[package]]
+category = "dev"
+description = "pytest: simple powerful testing with Python"
+name = "pytest"
+optional = false
+python-versions = ">=3.5"
+version = "5.2.2"
+
+[package.dependencies]
+atomicwrites = ">=1.0"
+attrs = ">=17.4.0"
+colorama = "*"
+more-itertools = ">=4.0.0"
+packaging = "*"
+pluggy = ">=0.12,<1.0"
+py = ">=1.5.0"
+wcwidth = "*"
+
+[package.dependencies.importlib-metadata]
+python = "<3.8"
+version = ">=0.12"
+
+[[package]]
+category = "main"
+description = "Extensions to the standard Python datetime module"
+name = "python-dateutil"
+optional = false
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
+version = "2.8.1"
+
+[package.dependencies]
+six = ">=1.5"
+
+[[package]]
+category = "main"
+description = "World timezone definitions, modern and historical"
+name = "pytz"
+optional = false
+python-versions = "*"
+version = "2019.3"
+
+[[package]]
+category = "main"
+description = "Q-CTRL Open Controls"
+name = "qctrl-open-controls"
+optional = false
+python-versions = ">=3.6.4,<3.8"
+version = "3.0.0"
+
+[package.dependencies]
+numpy = ">=1.16,<2.0"
+scipy = ">=1.3,<2.0"
+toml = ">=0.10.0,<0.11.0"
+
+[[package]]
+category = "main"
+description = "Python HTTP for Humans."
+name = "requests"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+version = "2.22.0"
+
+[package.dependencies]
+certifi = ">=2017.4.17"
+chardet = ">=3.0.2,<3.1.0"
+idna = ">=2.5,<2.9"
+urllib3 = ">=1.21.1,<1.25.0 || >1.25.0,<1.25.1 || >1.25.1,<1.26"
+
+[[package]]
+category = "main"
+description = "Pure-Python RSA implementation"
+name = "rsa"
+optional = false
+python-versions = "*"
+version = "4.0"
+
+[package.dependencies]
+pyasn1 = ">=0.1.3"
+
+[[package]]
+category = "main"
+description = "SciPy: Scientific Library for Python"
+name = "scipy"
+optional = false
+python-versions = ">=3.5"
+version = "1.3.2"
+
+[package.dependencies]
+numpy = ">=1.13.3"
+
+[[package]]
+category = "main"
+description = "Python 2 and 3 compatibility utilities"
+name = "six"
+optional = false
+python-versions = ">=2.6, !=3.0.*, !=3.1.*"
+version = "1.13.0"
+
+[[package]]
+category = "dev"
+description = "This package provides 26 stemmers for 25 languages generated from Snowball algorithms."
+name = "snowballstemmer"
+optional = false
+python-versions = "*"
+version = "2.0.0"
+
+[[package]]
+category = "main"
+description = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set"
+name = "sortedcontainers"
+optional = false
+python-versions = "*"
+version = "2.1.0"
+
+[[package]]
+category = "dev"
+description = "Python documentation generator"
+name = "sphinx"
+optional = false
+python-versions = ">=3.5"
+version = "2.2.1"
+
+[package.dependencies]
+Jinja2 = ">=2.3"
+Pygments = ">=2.0"
+alabaster = ">=0.7,<0.8"
+babel = ">=1.3,<2.0 || >2.0"
+colorama = ">=0.3.5"
+docutils = ">=0.12"
+imagesize = "*"
+packaging = "*"
+requests = ">=2.5.0"
+setuptools = "*"
+snowballstemmer = ">=1.1"
+sphinxcontrib-applehelp = "*"
+sphinxcontrib-devhelp = "*"
+sphinxcontrib-htmlhelp = "*"
+sphinxcontrib-jsmath = "*"
+sphinxcontrib-qthelp = "*"
+sphinxcontrib-serializinghtml = "*"
+
+[[package]]
+category = "dev"
+description = ""
+name = "sphinxcontrib-applehelp"
+optional = false
+python-versions = "*"
+version = "1.0.1"
+
+[[package]]
+category = "dev"
+description = ""
+name = "sphinxcontrib-devhelp"
+optional = false
+python-versions = "*"
+version = "1.0.1"
+
+[[package]]
+category = "dev"
+description = ""
+name = "sphinxcontrib-htmlhelp"
+optional = false
+python-versions = "*"
+version = "1.0.2"
+
+[[package]]
+category = "dev"
+description = "A sphinx extension which renders display math in HTML via JavaScript"
+name = "sphinxcontrib-jsmath"
+optional = false
+python-versions = ">=3.5"
+version = "1.0.1"
+
+[[package]]
+category = "dev"
+description = ""
+name = "sphinxcontrib-qthelp"
+optional = false
+python-versions = "*"
+version = "1.0.2"
+
+[[package]]
+category = "dev"
+description = ""
+name = "sphinxcontrib-serializinghtml"
+optional = false
+python-versions = "*"
+version = "1.1.3"
+
+[[package]]
+category = "main"
+description = "Computer algebra system (CAS) in Python"
+name = "sympy"
+optional = false
+python-versions = "*"
+version = "1.4"
+
+[package.dependencies]
+mpmath = ">=0.19"
+
+[[package]]
+category = "main"
+description = "Python Library for Tom's Obvious, Minimal Language"
+name = "toml"
+optional = false
+python-versions = "*"
+version = "0.10.0"
+
+[[package]]
+category = "dev"
+description = "a fork of Python 2 and 3 ast modules with type comment support"
+marker = "implementation_name == \"cpython\" and python_version < \"3.8\""
+name = "typed-ast"
+optional = false
+python-versions = "*"
+version = "1.4.0"
+
+[[package]]
+category = "main"
+description = "Backported and Experimental Type Hints for Python 3.5+"
+name = "typing-extensions"
+optional = false
+python-versions = "*"
+version = "3.7.4.1"
+
+[[package]]
+category = "main"
+description = "URI templates"
+name = "uritemplate"
+optional = false
+python-versions = "*"
+version = "3.0.0"
+
+[[package]]
+category = "main"
+description = "HTTP library with thread-safe connection pooling, file post, and more."
+name = "urllib3"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, <4"
+version = "1.25.7"
+
+[[package]]
+category = "dev"
+description = "Measures number of Terminal column cells of wide-character codes"
+name = "wcwidth"
+optional = false
+python-versions = "*"
+version = "0.1.7"
+
+[[package]]
+category = "dev"
+description = "Module for decorators, wrappers and monkey patching."
+name = "wrapt"
+optional = false
+python-versions = "*"
+version = "1.11.2"
+
+[[package]]
+category = "dev"
+description = "Backport of pathlib-compatible object wrapper for zip files"
+marker = "python_version < \"3.8\""
+name = "zipp"
+optional = false
+python-versions = ">=2.7"
+version = "0.6.0"
+
+[package.dependencies]
+more-itertools = "*"
+
+[metadata]
+content-hash = "052e7f175e2081d9ba756841e051f060976e42ec0f1cf6f15f32dbe4927d4aab"
+python-versions = ">=3.6.4,<3.8"
+
+[metadata.hashes]
+alabaster = ["446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359", "a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"]
+astroid = ["71ea07f44df9568a75d0f354c49143a4575d90645e9fead6dfb52c26a85ed13a", "840947ebfa8b58f318d42301cf8c0a20fd794a33b61cc4638e28e9e61ba32f42"]
+atomicwrites = ["03472c30eb2c5d1ba9227e4c2ca66ab8287fbfbbda3888aa93dc2e28fc6811b4", "75a9445bac02d8d058d5e1fe689654ba5a6556a1dfd8ce6ec55a0ed79866cfa6"]
+attrs = ["08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c", "f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"]
+babel = ["af92e6106cb7c55286b25b38ad7695f8b4efb36a90ba483d7f7a6628c46158ab", "e86135ae101e31e2c8ec20a4e0c5220f4eed12487d5cf3f78be7e98d3a57fc28"]
+cachetools = ["428266a1c0d36dc5aca63a2d7c5942e88c2c898d72139fca0e97fdd2380517ae", "8ea2d3ce97850f31e4a08b0e2b5e6c34997d7216a9d2c98e0f3978630d4da69a"]
+certifi = ["e4f3620cfea4f83eedc95b24abd9cd56f3c4b146dd0177e83a21b4eb49e21e50", "fd7c7c74727ddcf00e9acd26bba8da604ffec95bf1c2144e67aff7a8b50e6cef"]
+chardet = ["84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", "fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"]
+cirq = ["474ff197d7071c76d23e332766ac0302d1ade25d4cd72a00b4d49dd3d1d5dcf2"]
+colorama = ["05eed71e2e327246ad6b38c540c4a3117230b19679b875190486ddd2d721422d", "f8ac84de7840f5b9c4e3347b3c1eaa50f7e49c2b07596221daec5edaabbd7c48"]
+cycler = ["1d8a5ae1ff6c5cf9b93e8811e581232ad8920aeec647c37316ceac982b08cb2d", "cd7b2d1018258d7247a71425e9f26463dfb444d411c39569972f4ce586b0c9d8"]
+dataclasses = ["454a69d788c7fda44efd71e259be79577822f5e3f53f029a22d08004e951dc9f", "6988bd2b895eef432d562370bb707d540f32f7360ab13da45340101bc2307d84"]
+decorator = ["54c38050039232e1db4ad7375cfce6748d7b41c29e95a081c8a6d2c30364a2ce", "5d19b92a3c8f7f101c8dd86afd86b0f061a8ce4540ab8cd401fa2542756bce6d"]
+docutils = ["6c4f696463b79f1fb8ba0c594b63840ebd41f059e92b31957c46b74a4599b6d0", "9e4d7ecfc600058e07ba661411a2b7de2fd0fafa17d1a7f7361cd47b1175c827", "a2aeea129088da402665e92e0b25b04b073c04b2dce4ab65caaa38b7ce2e1a99"]
+google-api-python-client = ["3121d55d106ef1a2756e8074239512055bd99eb44da417b3dd680f9a1385adec", "a8a88174f66d92aed7ebbd73744c2c319b4b1ce828e565f9ec721352d2e2fb8c"]
+google-auth = ["8b67e34a07055b9785948ff9d3e044f93be9019f4f69711b04450087ae150817", "cf60c71698f90177e044c8df1e2915a6da372a99d2af0e236d76c426aaf4f114"]
+google-auth-httplib2 = ["098fade613c25b4527b2c08fa42d11f3c2037dda8995d86de0745228e965d445", "f1c437842155680cf9918df9bc51c1182fda41feef88c34004bd1978c8157e08"]
+httplib2 = ["34537dcdd5e0f2386d29e0e2c6d4a1703a3b982d34c198a5102e6e5d6194b107", "409fa5509298f739b34d5a652df762cb0042507dc93f6633e306b11289d6249d"]
+idna = ["c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407", "ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c"]
+imagesize = ["3f349de3eb99145973fefb7dbe38554414e5c30abd0c8e4b970a7c9d09f3a1d8", "f3832918bc3c66617f92e35f5d70729187676313caa60c187eb0f28b8fe5e3b5"]
+importlib-metadata = ["aa18d7378b00b40847790e7c27e11673d7fed219354109d0e7b9e5b25dc3ad26", "d5f18a79777f3aa179c145737780282e27b508fc8fd688cb17c7a813e8bd39af"]
+isort = ["54da7e92468955c4fceacd0c86bd0ec997b0e1ee80d97f67c35a78b719dccab1", "6e811fcb295968434526407adb8796944f1988c5b65e8139058f2014cbe100fd"]
+jinja2 = ["74320bb91f31270f9551d46522e33af46a80c3d619f4a4bf42b3164d30b5911f", "9fe95f19286cfefaa917656583d020be14e7859c6b0252588391e47db34527de"]
+kiwisolver = ["05b5b061e09f60f56244adc885c4a7867da25ca387376b02c1efc29cc16bcd0f", "26f4fbd6f5e1dabff70a9ba0d2c4bd30761086454aa30dddc5b52764ee4852b7", "3b2378ad387f49cbb328205bda569b9f87288d6bc1bf4cd683c34523a2341efe", "400599c0fe58d21522cae0e8b22318e09d9729451b17ee61ba8e1e7c0346565c", "47b8cb81a7d18dbaf4fed6a61c3cecdb5adec7b4ac292bddb0d016d57e8507d5", "53eaed412477c836e1b9522c19858a8557d6e595077830146182225613b11a75", "58e626e1f7dfbb620d08d457325a4cdac65d1809680009f46bf41eaf74ad0187", "5a52e1b006bfa5be04fe4debbcdd2688432a9af4b207a3f429c74ad625022641", "5c7ca4e449ac9f99b3b9d4693debb1d6d237d1542dd6a56b3305fe8a9620f883", "682e54f0ce8f45981878756d7203fd01e188cc6c8b2c5e2cf03675390b4534d5", "79bfb2f0bd7cbf9ea256612c9523367e5ec51d7cd616ae20ca2c90f575d839a2", "7f4dd50874177d2bb060d74769210f3bce1af87a8c7cf5b37d032ebf94f0aca3", "8944a16020c07b682df861207b7e0efcd2f46c7488619cb55f65882279119389", "8aa7009437640beb2768bfd06da049bad0df85f47ff18426261acecd1cf00897", "939f36f21a8c571686eb491acfffa9c7f1ac345087281b412d63ea39ca14ec4a", "9733b7f64bd9f807832d673355f79703f81f0b3e52bfce420fc00d8cb28c6a6c", "a02f6c3e229d0b7220bd74600e9351e18bc0c361b05f29adae0d10599ae0e326", "a0c0a9f06872330d0dd31b45607197caab3c22777600e88031bfe66799e70bb0", "acc4df99308111585121db217681f1ce0eecb48d3a828a2f9bbf9773f4937e9e", "b64916959e4ae0ac78af7c3e8cef4becee0c0e9694ad477b4c6b3a536de6a544", "d3fcf0819dc3fea58be1fd1ca390851bdb719a549850e708ed858503ff25d995", "d52e3b1868a4e8fd18b5cb15055c76820df514e26aa84cc02f593d99fef6707f", "db1a5d3cc4ae943d674718d6c47d2d82488ddd94b93b9e12d24aabdbfe48caee", "e3a21a720791712ed721c7b95d433e036134de6f18c77dbe96119eaf7aa08004", "e8bf074363ce2babeb4764d94f8e65efd22e6a7c74860a4f05a6947afc020ff2", "f16814a4a96dc04bf1da7d53ee8d5b1d6decfc1a92a63349bb15d37b6a263dd9", "f2b22153870ca5cf2ab9c940d7bc38e8e9089fa0f7e5856ea195e1cf4ff43d5a", "f790f8b3dff3d53453de6a7b7ddd173d2e020fb160baff578d578065b108a05f"]
+lazy-object-proxy = ["0c4b206227a8097f05c4dbdd323c50edf81f15db3b8dc064d08c62d37e1a504d", "194d092e6f246b906e8f70884e620e459fc54db3259e60cf69a4d66c3fda3449", "1be7e4c9f96948003609aa6c974ae59830a6baecc5376c25c92d7d697e684c08", "4677f594e474c91da97f489fea5b7daa17b5517190899cf213697e48d3902f5a", "48dab84ebd4831077b150572aec802f303117c8cc5c871e182447281ebf3ac50", "5541cada25cd173702dbd99f8e22434105456314462326f06dba3e180f203dfd", "59f79fef100b09564bc2df42ea2d8d21a64fdcda64979c0fa3db7bdaabaf6239", "8d859b89baf8ef7f8bc6b00aa20316483d67f0b1cbf422f5b4dc56701c8f2ffb", "9254f4358b9b541e3441b007a0ea0764b9d056afdeafc1a5569eee1cc6c1b9ea", "9651375199045a358eb6741df3e02a651e0330be090b3bc79f6d0de31a80ec3e", "97bb5884f6f1cdce0099f86b907aa41c970c3c672ac8b9c8352789e103cf3156", "9b15f3f4c0f35727d3a0fba4b770b3c4ebbb1fa907dbcc046a1d2799f3edd142", "a2238e9d1bb71a56cd710611a1614d1194dc10a175c1e08d75e1a7bcc250d442", "a6ae12d08c0bf9909ce12385803a543bfe99b95fe01e752536a60af2b7797c62", "ca0a928a3ddbc5725be2dd1cf895ec0a254798915fb3a36af0964a0a4149e3db", "cb2c7c57005a6804ab66f106ceb8482da55f5314b7fcb06551db1edae4ad1531", "d74bb8693bf9cf75ac3b47a54d716bbb1a92648d5f781fc799347cfc95952383", "d945239a5639b3ff35b70a88c5f2f491913eb94871780ebfabb2568bd58afc5a", "eba7011090323c1dadf18b3b689845fd96a61ba0a1dfbd7f24b921398affc357", "efa1909120ce98bbb3777e8b6f92237f5d5c8ea6758efea36a473e1d38f7d3e4", "f3900e8a5de27447acbf900b4750b0ddfd7ec1ea7fbaf11dfa911141bc522af0"]
+markupsafe = ["00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473", "09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161", "09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235", "1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5", "24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff", "29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b", "43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1", "46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e", "500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183", "535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66", "62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1", "6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1", "717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e", "79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b", "7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905", "88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735", "8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d", "98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e", "9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d", "9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c", "ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21", "b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2", "b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5", "b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b", "ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6", "c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f", "cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f", "e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"]
+matplotlib = ["1febd22afe1489b13c6749ea059d392c03261b2950d1d45c17e3aed812080c93", "31a30d03f39528c79f3a592857be62a08595dec4ac034978ecd0f814fa0eec2d", "4442ce720907f67a79d45de9ada47be81ce17e6c2f448b3c64765af93f6829c9", "796edbd1182cbffa7e1e7a97f1e141f875a8501ba8dd834269ae3cd45a8c976f", "934e6243df7165aad097572abf5b6003c77c9b6c480c3c4de6f2ef1b5fdd4ec0", "bab9d848dbf1517bc58d1f486772e99919b19efef5dd8596d4b26f9f5ee08b6b", "c1fe1e6cdaa53f11f088b7470c2056c0df7d80ee4858dadf6cbe433fcba4323b", "e5b8aeca9276a3a988caebe9f08366ed519fff98f77c6df5b64d7603d0e42e36", "ec6bd0a6a58df3628ff269978f4a4b924a0d371ad8ce1f8e2b635b99e482877a"]
+mccabe = ["ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42", "dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"]
+more-itertools = ["409cd48d4db7052af495b09dec721011634af3753ae1ef92d2b32f73a745f832", "92b8c4b06dac4f0611c0729b2f2ede52b2e1bac1ab48f089c7ddc12e26bb60c4"]
+mpmath = ["fc17abe05fbab3382b61a123c398508183406fa132e0223874578e20946499f6"]
+networkx = ["8311ddef63cf5c5c5e7c1d0212dd141d9a1fe3f474915281b73597ed5f1d4e3d"]
+numpy = ["0a7a1dd123aecc9f0076934288ceed7fd9a81ba3919f11a855a7887cbe82a02f", "0c0763787133dfeec19904c22c7e358b231c87ba3206b211652f8cbe1241deb6", "3d52298d0be333583739f1aec9026f3b09fdfe3ddf7c7028cb16d9d2af1cca7e", "43bb4b70585f1c2d153e45323a886839f98af8bfa810f7014b20be714c37c447", "475963c5b9e116c38ad7347e154e5651d05a2286d86455671f5b1eebba5feb76", "64874913367f18eb3013b16123c9fed113962e75d809fca5b78ebfbb73ed93ba", "683828e50c339fc9e68720396f2de14253992c495fdddef77a1e17de55f1decc", "6ca4000c4a6f95a78c33c7dadbb9495c10880be9c89316aa536eac359ab820ae", "75fd817b7061f6378e4659dd792c84c0b60533e867f83e0d1e52d5d8e53df88c", "7d81d784bdbed30137aca242ab307f3e65c8d93f4c7b7d8f322110b2e90177f9", "8d0af8d3664f142414fd5b15cabfd3b6cc3ef242a3c7a7493257025be5a6955f", "9679831005fb16c6df3dd35d17aa31dc0d4d7573d84f0b44cc481490a65c7725", "a8f67ebfae9f575d85fa859b54d3bdecaeece74e3274b0b5c5f804d7ca789fe1", "acbf5c52db4adb366c064d0b7c7899e3e778d89db585feadd23b06b587d64761", "ada4805ed51f5bcaa3a06d3dd94939351869c095e30a2b54264f5a5004b52170", "c7354e8f0eca5c110b7e978034cd86ed98a7a5ffcf69ca97535445a595e07b8e", "e2e9d8c87120ba2c591f60e32736b82b67f72c37ba88a4c23c81b5b8fa49c018", "e467c57121fe1b78a8f68dd9255fbb3bb3f4f7547c6b9e109f31d14569f490c3", "ede47b98de79565fcd7f2decb475e2dcc85ee4097743e551fe26cfc7eb3ff143", "f58913e9227400f1395c7b800503ebfdb0772f1c33ff8cb4d6451c06cabdf316", "fe39f5fd4103ec4ca3cb8600b19216cd1ff316b4990f4c0b6057ad982c0a34d5"]
+packaging = ["28b924174df7a2fa32c1953825ff29c61e2f5e082343165438812f00d3a7fc47", "d9551545c6d761f3def1677baf08ab2a3ca17c56879e70fecba2fc4dde4ed108"]
+pandas = ["00dff3a8e337f5ed7ad295d98a31821d3d0fe7792da82d78d7fd79b89c03ea9d", "22361b1597c8c2ffd697aa9bf85423afa9e1fcfa6b1ea821054a244d5f24d75e", "255920e63850dc512ce356233081098554d641ba99c3767dde9e9f35630f994b", "26382aab9c119735908d94d2c5c08020a4a0a82969b7e5eefb92f902b3b30ad7", "33970f4cacdd9a0ddb8f21e151bfb9f178afb7c36eb7c25b9094c02876f385c2", "4545467a637e0e1393f7d05d61dace89689ad6d6f66f267f86fff737b702cce9", "52da74df8a9c9a103af0a72c9d5fdc8e0183a90884278db7f386b5692a2220a4", "61741f5aeb252f39c3031d11405305b6d10ce663c53bc3112705d7ad66c013d0", "6a3ac2c87e4e32a969921d1428525f09462770c349147aa8e9ab95f88c71ec71", "7458c48e3d15b8aaa7d575be60e1e4dd70348efcd9376656b72fecd55c59a4c3", "78bf638993219311377ce9836b3dc05f627a666d0dbc8cec37c0ff3c9ada673b", "8153705d6545fd9eb6dd2bc79301bff08825d2e2f716d5dced48daafc2d0b81f", "975c461accd14e89d71772e89108a050fa824c0b87a67d34cedf245f6681fc17", "9962957a27bfb70ab64103d0a7b42fa59c642fb4ed4cb75d0227b7bb9228535d", "adc3d3a3f9e59a38d923e90e20c4922fc62d1e5a03d083440468c6d8f3f1ae0a", "bbe3eb765a0b1e578833d243e2814b60c825b7fdbf4cdfe8e8aae8a08ed56ecf", "df8864824b1fe488cf778c3650ee59c3a0d8f42e53707de167ba6b4f7d35f133", "e45055c30a608076e31a9fcd780a956ed3b1fa20db61561b8d88b79259f526f7", "ee50c2142cdcf41995655d499a157d0a812fce55c97d9aad13bc1eef837ed36c"]
+pluggy = ["0db4b7601aae1d35b4a033282da476845aa19185c1e6964b25cf324b5e4ec3e6", "fa5fa1622fa6dd5c030e9cad086fa19ef6a0cf6d7a2d12318e10cb49d6d68f34"]
+protobuf = ["03f43eac9d5b651f976e91cf46a25b75e5779d98f0f4114b0abfed83376d75f8", "0c94b21e6de01362f91a86b372555d22a60b59708599ca9d5032ae9fdf8e3538", "2d2a9f30f61f4063fadd7fb68a2510a6939b43c0d6ceeec5c4704f22225da28e", "34a0b05fca061e4abb77dd180209f68d8637115ff319f51e28a6a9382d69853a", "358710fd0db25372edcf1150fa691f48376a134a6c69ce29f38f185eea7699e6", "3761ab21883f1d3add8643413b326a0026776879b13ecf904e1e05fe18532c03", "41e47198b94c27ba05a08b4a95160656105745c462af574e4bcb0807164065c0", "8c61cc8a76e9d381c665aecc5105fa0f1878cf7db8b5cd17202603bcb386d0fc", "a6eebc4db759e58fdac02efcd3028b811effac881d8a5bad1996e4e8ee6acb47", "a9c12f7c98093da0a46ba76ec40ace725daa1ac4038c41e4b1466afb5c45bb01", "cb95068492ba0859b8c9e61fa8ba206a83c64e5d0916fb4543700b2e2b214115", "cd98476ce7bb4dcd6a7b101f5eecdc073dafea19f311e36eb8fba1a349346277", "ce64cfbea18c535176bdaa10ba740c0fc4c6d998a3f511c17bedb0ae4b3b167c", "dcbb59eac73fd454e8f2c5fba9e3d3320fd4707ed6a9d3ea3717924a6f0903ea", "dd67f34458ae716029e2a71ede998e9092493b62a519236ca52e3c5202096c87", "e3c96056eb5b7284a20e256cb0bf783c8f36ad82a4ae5434a7b7cd02384144a7", "f612d584d7a27e2f39e7b17878430a959c1bc09a74ba09db096b468558e5e126", "f6de8a7d6122297b81566e5bd4df37fd5d62bec14f8f90ebff8ede1c9726cd0a", "fa529d9261682b24c2aaa683667253175c9acebe0a31105394b221090da75832"]
+py = ["64f65755aee5b381cea27766a3a147c3f15b9b6b9ac88676de66ba2ae36793fa", "dc639b046a6e2cff5bbe40194ad65936d6ba360b52b3c3fe1d08a82dd50b5e53"]
+pyasn1 = ["1321d4b2f051410fe7302bb1619903d30b24ba1451d019c11d242d11b2a35444", "2860a047f666afd23b197a65f33145313511c368ce919b2d9b1853ffd3e9d32d", "2919babd43b3b44247c23201b71072c0c65a636daa595cad5bcd276094dbfc2d", "437a23121602c0bb6c65320b27e31e334ffd73a9ca5c6c075b66b6270b1a8184", "5a89df3c62688261e27439d5715fd0d3ca6bf7bf1067e2171642e92aff17e817", "62cdade8b5530f0b185e09855dd422bc05c0bbff6b72ff61381c09dac7befd8c", "67a43aec85f4ea96e72a7b22227ba7a45cf03b7297e1a53418be164bbf68335e", "813b198c169e9442f340743f77093435bf3e1de8d1731f3abc45d44afba17556", "96c44b5604e7674e53e27fce98f3fc68821d9546151b98842c27b533122649da", "a9495356ca1d66ed197a0f72b41eb1823cf7ea8b5bd07191673e8147aecf8604", "bcac468e38d16e94fee4c8f76eef1feb9a06a911e93465f2351a4140fa66d303", "c39d11c72f0e5e71faa35c8c8ef5ee9b810ec99a3c64f05133f1325fe5636bba", "f124185ccc1c1c5e782aa58d46bc28be279673a482334d70de6735d05d8b4b10"]
+pyasn1-modules = ["0c35a52e00b672f832e5846826f1fb7507907f7d52fba6faa9e3c4cbe874fe4b", "13a6955947d8a554de78fc305a4d651f20fb5580b88612a5f0661d4f189d27ac", "233f55c840e821e76828262db976ac894b285909d22d060c2bdb522e7bf28cc6", "24d54188cb7abd750e0a2cba61b7b46a75608175a0c3c1b1eee08322915d8d21", "27581362b4253b9c999882a64df974124cde12be0bf2c04148a0d68bc6bbb7b8", "33c220a2701032261a23eea6e9881404ac6fc7ff96f183b5353fea8fc8962547", "64f6aecf26e93f6a3ba3725b4eb9f532551747d7a63ca9ff43aef12f4bf11eac", "7b4edf07ca2f759d7cf693184be09f22e067c2eb52b03c770d0a2e9de1c67dfd", "9b972f81f59d896cebb9ebb1d44296f1acb28bf7869443c37551f4eed8d74f83", "9ca5e376a6d9dee35bb3a62608dfa2e6698798aa6b8db3c7afd0eb31af0d63c7", "b6ada4f840fe51abf5a6bd545b45bf537bea62221fa0dde2e8a553ed9f06a4e3", "c14b107a67ee36a7f183ae9f4803ffde4a03b67f3192eab0a62e851af71371d3", "eaf35047a0b068e3e0c2a99618b13b65c98c329661daa78c9d44a4ef0fe8139e"]
+pycodestyle = ["95a2219d12372f05704562a14ec30bc76b05a5b297b21a5dfe3f6fac3491ae56", "e40a936c9a450ad81df37f549d676d127b1b66000a6c500caa2b085bc0ca976c"]
+pydocstyle = ["04c84e034ebb56eb6396c820442b8c4499ac5eb94a3bda88951ac3dc519b6058", "66aff87ffe34b1e49bff2dd03a88ce6843be2f3346b0c9814410d34987fbab59"]
+pyflakes = ["17dbeb2e3f4d772725c777fabc446d5634d1038f234e77343108ce445ea69ce0", "d976835886f8c5b31d47970ed689944a0262b5f3afa00a5a7b4dc81e5449f8a2"]
+pygments = ["71e430bc85c88a430f000ac1d9b331d2407f681d6f6aec95e8bcfbc3df5b0127", "881c4c157e45f30af185c1ffe8d549d48ac9127433f2c380c24b84572ad66297"]
+pylama = ["9bae53ef9c1a431371d6a8dca406816a60d547147b60a4934721898f553b7d8f", "fd61c11872d6256b019ef1235be37b77c922ef37ac9797df6bd489996dddeb15"]
+pylint = ["7b76045426c650d2b0f02fc47c14d7934d17898779da95288a74c2a7ec440702", "856476331f3e26598017290fd65bebe81c960e806776f324093a46b76fb2d1c0"]
+pylint-runner = ["9e04d72471a9225db6734334ec578ac37b47130625553df149ca6a20ecd565a9", "bc44a39ad93ffa865f282dff26632ac78b60ce53c417d4b1de82b52842d1e6e8"]
+pyparsing = ["20f995ecd72f2a1f4bf6b072b63b22e2eb457836601e76d6e5dfcd75436acc1f", "4ca62001be367f01bd3e92ecbb79070272a9d4964dce6a48a82ff0b8bc7e683a"]
+pytest = ["27abc3fef618a01bebb1f0d6d303d2816a99aa87a5968ebc32fe971be91eb1e6", "58cee9e09242937e136dbb3dab466116ba20d6b7828c7620f23947f37eb4dae4"]
+python-dateutil = ["73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c", "75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"]
+pytz = ["1c557d7d0e871de1f5ccd5833f60fb2550652da6be2693c1e02300743d21500d", "b02c06db6cf09c12dd25137e563b31700d3b80fcc4ad23abb7a315f2789819be"]
+qctrl-open-controls = ["3b85c8b1dda1f01a54b782c318092ea799f5aa3d6ce00bad5c1cbf8b33d59724", "847bb5eebf1bd4174529b277cd8c286b4655e08dcc7644a5594db3232cdc8fd9"]
+requests = ["11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4", "9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31"]
+rsa = ["14ba45700ff1ec9eeb206a2ce76b32814958a98e372006c8fb76ba820211be66", "1a836406405730121ae9823e19c6e806c62bbad73f890574fff50efa4122c487"]
+scipy = ["0359576d8cc058bd615999cf985e2423dc6cc824666d60e8b8d4810569a04655", "07673b5b96dbe28c88f3a53ca9af67f802aa853de7402e31f473b4dd6501c799", "0f81e71149539ac09053a3f9165659367b060eceef3bbde11e6600e1c341f1f2", "125aa82f7b3d4bd7f77fed6c3c6e31be47e33f129d829799569389ae59f913e7", "2dc26e5b3eb86b7adad506b6b04020f6a87e1102c9acd039e937d28bdcee7fa6", "2e4b5fdb635dd425bf46fbd6381612692d3c795f1eb6fe62410305a440691d46", "33ac3213ee617bbc0eac84d02b130d69093ed7738afb281dfdeb12a9dbdf1530", "34c48d922760782732d6f8f4532e320984d1280763c6787c6582021d34c8ad79", "3f556f63e070e9596624e42e99d23b259d8f0fc63ec093bef97a9f1c579565b2", "470d8fc76ccab6cfff60a9de4ce316a23ee7f63615d948c7446dc7c1bb45042d", "4ad7a3ae9831d2085d6f50b81bfcd76368293eafdf31f4ac9f109c6061309c24", "61812a7db0d9bc3f13653e52b8ddb1935cf444ec55f39160fc2778aeb2719057", "7a0477929e6f9d5928fe81fe75d00b7da9545a49109e66028d85848b18aeef99", "9c3221039da50f3b60da70b65d6b020ea26cefbb097116cfec696010432d1f6c", "a03939b431994289f39373c57bbe452974a7da724ae7f9620a1beee575434da4", "df4dbd3d40db3f667e0145dba5f50954bf28b2dd5b8b400c79d5e3fe8cb67ce2", "e837c8068bd1929a533e9d51562faf6584ddb5303d9e218d8c11aa4719dcd617", "ecfd45ca0ce1d6c13bef17794b4052cc9a9574f4be8d44c9bcfd7e34294bd2d7", "ee5888c62cd83c9bf9927ffcee08434e7d5c81a8f31e5b85af5470e511022c08", "f018892621b787b9abf76d51d1f0c21611c71752ebb1891ccf7992e0bf973708", "f2d5db81d90d14a32d4aff920f52fca5639bcaaaf87b4f61bce83a1d238f49fc"]
+six = ["1f1b7d42e254082a9db6279deae68afb421ceba6158efa6131de7b3003ee93fd", "30f610279e8b2578cab6db20741130331735c781b56053c59c4076da27f06b66"]
+snowballstemmer = ["209f257d7533fdb3cb73bdbd24f436239ca3b2fa67d56f6ff88e86be08cc5ef0", "df3bac3df4c2c01363f3dd2cfa78cce2840a79b9f1c2d2de9ce8d31683992f52"]
+sortedcontainers = ["974e9a32f56b17c1bac2aebd9dcf197f3eb9cd30553c5852a3187ad162e1a03a", "d9e96492dd51fae31e60837736b38fe42a187b5404c16606ff7ee7cd582d4c60"]
+sphinx = ["31088dfb95359384b1005619827eaee3056243798c62724fd3fa4b84ee4d71bd", "52286a0b9d7caa31efee301ec4300dbdab23c3b05da1c9024b4e84896fb73d79"]
+sphinxcontrib-applehelp = ["edaa0ab2b2bc74403149cb0209d6775c96de797dfd5b5e2a71981309efab3897", "fb8dee85af95e5c30c91f10e7eb3c8967308518e0f7488a2828ef7bc191d0d5d"]
+sphinxcontrib-devhelp = ["6c64b077937330a9128a4da74586e8c2130262f014689b4b89e2d08ee7294a34", "9512ecb00a2b0821a146736b39f7aeb90759834b07e81e8cc23a9c70bacb9981"]
+sphinxcontrib-htmlhelp = ["4670f99f8951bd78cd4ad2ab962f798f5618b17675c35c5ac3b2132a14ea8422", "d4fd39a65a625c9df86d7fa8a2d9f3cd8299a3a4b15db63b50aac9e161d8eff7"]
+sphinxcontrib-jsmath = ["2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178", "a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"]
+sphinxcontrib-qthelp = ["513049b93031beb1f57d4daea74068a4feb77aa5630f856fcff2e50de14e9a20", "79465ce11ae5694ff165becda529a600c754f4bc459778778c7017374d4d406f"]
+sphinxcontrib-serializinghtml = ["c0efb33f8052c04fd7a26c0a07f1678e8512e0faec19f4aa8f2473a8b81d5227", "db6615af393650bf1151a6cd39120c29abaf93cc60db8c48eb2dddbfdc3a9768"]
+sympy = ["71a11e5686ae7ab6cb8feb5bd2651ef4482f8fd43a7c27e645a165e4353b23e1", "f9b00ec76151c98470e84f1da2d7d03633180b71fb318428ddccce1c867d3eaa"]
+toml = ["229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c", "235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e", "f1db651f9657708513243e61e6cc67d101a39bad662eaa9b5546f789338e07a3"]
+typed-ast = ["18511a0b3e7922276346bcb47e2ef9f38fb90fd31cb9223eed42c85d1312344e", "262c247a82d005e43b5b7f69aff746370538e176131c32dda9cb0f324d27141e", "2b907eb046d049bcd9892e3076c7a6456c93a25bebfe554e931620c90e6a25b0", "354c16e5babd09f5cb0ee000d54cfa38401d8b8891eefa878ac772f827181a3c", "4e0b70c6fc4d010f8107726af5fd37921b666f5b31d9331f0bd24ad9a088e631", "630968c5cdee51a11c05a30453f8cd65e0cc1d2ad0d9192819df9978984529f4", "66480f95b8167c9c5c5c87f32cf437d585937970f3fc24386f313a4c97b44e34", "71211d26ffd12d63a83e079ff258ac9d56a1376a25bc80b1cdcdf601b855b90b", "95bd11af7eafc16e829af2d3df510cecfd4387f6453355188342c3e79a2ec87a", "bc6c7d3fa1325a0c6613512a093bc2a2a15aeec350451cbdf9e1d4bffe3e3233", "cc34a6f5b426748a507dd5d1de4c1978f2eb5626d51326e43280941206c209e1", "d755f03c1e4a51e9b24d899561fec4ccaf51f210d52abdf8c07ee2849b212a36", "d7c45933b1bdfaf9f36c579671fec15d25b06c8398f113dab64c18ed1adda01d", "d896919306dd0aa22d0132f62a1b78d11aaf4c9fc5b3410d3c666b818191630a", "ffde2fbfad571af120fcbfbbc61c72469e72f550d676c3342492a9dfdefb8f12"]
+typing-extensions = ["091ecc894d5e908ac75209f10d5b4f118fbdb2eb1ede6a63544054bb1edb41f2", "910f4656f54de5993ad9304959ce9bb903f90aadc7c67a0bef07e678014e892d", "cf8b63fedea4d89bab840ecbb93e75578af28f76f66c35889bd7065f5af88575"]
+uritemplate = ["01c69f4fe8ed503b2951bef85d996a9d22434d2431584b5b107b2981ff416fbd", "1b9c467a940ce9fb9f50df819e8ddd14696f89b9a8cc87ac77952ba416e0a8fd", "c02643cebe23fc8adb5e6becffe201185bf06c40bda5c0b4028a93f1527d011d"]
+urllib3 = ["a8a318824cc77d1fd4b2bec2ded92646630d7fe8619497b142c84a9e6f5a7293", "f3c5fd51747d450d4dcf6f923c81f78f811aab8205fda64b0aba34a4e48b0745"]
+wcwidth = ["3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e", "f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c"]
+wrapt = ["565a021fd19419476b9362b05eeaa094178de64f8361e44468f9e9d7843901e1"]
+zipp = ["3718b1cbcd963c7d4c5511a8240812904164b7f381b647143a89d3b98f9bcd8e", "f06903e9f1f43b12d371004b4ac7b06ab39a44adc747266928ae6debfa7b3335"]
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..3f892bb
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,69 @@
+[tool.poetry]
+name = "qctrl-cirq"
+version = "0.0.1rc5"
+description = "Q-CTRL Cirq Adapter"
+license = "Apache-2.0"
+authors = ["Q-CTRL "]
+readme = "README.md"
+keywords = [
+ "quantum",
+ "computing",
+ "open source",
+ "engineering",
+ "cirq"
+]
+classifiers = [
+ "Development Status :: 5 - Production/Stable",
+ "Environment :: Console",
+ "Intended Audience :: Developers",
+ "Intended Audience :: Education",
+ "Intended Audience :: Science/Research",
+ "License :: OSI Approved :: Apache Software License",
+ "Natural Language :: English",
+ "Operating System :: OS Independent",
+ "Programming Language :: Python :: 3.6",
+ "Topic :: Scientific/Engineering :: Physics",
+ "Topic :: Scientific/Engineering :: Visualization",
+ "Topic :: Software Development :: Embedded Systems",
+ "Topic :: System :: Distributed Computing"
+ ]
+repository = "https://github.com/qctrl/python-cirq"
+packages = [
+ { include = "qctrlcirq" },
+]
+
+[tool.poetry.dependencies] # https://poetry.eustace.io/docs/versions
+python = ">=3.6.4,<3.8"
+numpy = "^1.16"
+scipy = "^1.3"
+toml = "^0.10.0"
+cirq = "^0.6.0"
+qctrl-open-controls = "^3.0.0"
+
+[tool.poetry.dev-dependencies]
+pytest = "*"
+pylama = "*"
+pylint = "*"
+pylint_runner = "*"
+sphinx = "^2.2.0"
+
+[tool.dephell.main]
+from = {format = "poetry", path = "pyproject.toml"}
+to = {format = "setuppy", path = "setup.py"}
+versioning = "pep"
+prereleases = true
+
+[build-system]
+requires = ["poetry>=0.12"]
+build-backend = "poetry.masonry.api"
+
+# _______________________________________
+# / If you update this file, please run \
+# \ poetry update && dephell deps convert /
+# ---------------------------------------
+# \ ^__^
+# \ (oo)\_______
+# (__)\ )\/\
+# ||----w |
+# || ||
+
diff --git a/qctrlcirq/__init__.py b/qctrlcirq/__init__.py
new file mode 100644
index 0000000..f0ff5f7
--- /dev/null
+++ b/qctrlcirq/__init__.py
@@ -0,0 +1,29 @@
+# Copyright 2019 Q-CTRL Pty Ltd & Q-CTRL Inc
+#
+# 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
+#
+# http://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.
+
+"""
+===========
+qctrlcirq
+===========
+"""
+
+__version__ = "0.0.1rc5"
+
+from .circuit import convert_dds_to_cirq_circuit
+
+from .schedule import convert_dds_to_cirq_schedule
+
+__all__ = [
+ 'convert_dds_to_cirq_circuit',
+ 'convert_dds_to_cirq_schedule']
diff --git a/qctrlcirq/circuit.py b/qctrlcirq/circuit.py
new file mode 100644
index 0000000..c57c2fa
--- /dev/null
+++ b/qctrlcirq/circuit.py
@@ -0,0 +1,191 @@
+# Copyright 2019 Q-CTRL Pty Ltd & Q-CTRL Inc
+#
+# 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
+#
+# http://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.
+
+"""
+============
+cirq.circuit
+============
+"""
+
+import numpy as np
+
+import cirq
+
+from qctrlopencontrols import DynamicDecouplingSequence
+
+from qctrlopencontrols.exceptions.exceptions import ArgumentsValueError
+from qctrlopencontrols.globals import (FIX_DURATION_UNITARY, INSTANT_UNITARY)
+
+
+def convert_dds_to_cirq_circuit(
+ dynamic_decoupling_sequence,
+ target_qubits=None,
+ gate_time=0.1,
+ add_measurement=True,
+ algorithm=INSTANT_UNITARY):
+ """Converts a Dynamic Decoupling Sequence into quantum circuit
+ as defined in cirq
+
+ Parameters
+ ----------
+ dynamic_decoupling_sequence : DynamicDecouplingSequence
+ The dynamic decoupling sequence
+ target_qubits : list, optional
+ List of target qubits for the sequence operation; the qubits must be
+ cirq.Qid type; defaults to None in which case a 1-D lattice of one
+ qubit is used (indexed as 0).
+ gate_time : float, optional
+ Time (in seconds) delay introduced by a gate; defaults to 0.1
+ add_measurement : bool, optional
+ If True, the circuit contains a measurement operation for each of the
+ target qubits. Measurement from each of the qubits is associated
+ with a string as key. The string is formatted as 'qubit-X' where
+ X is a number between 0 and len(target_qubits).
+ algorithm : str, optional
+ One of 'fixed duration unitary' or 'instant unitary'; In the case of
+ 'fixed duration unitary', the sequence operations are assumed to be
+ taking the amount of gate_time while 'instant unitary' assumes the sequence
+ operations are instantaneous (and hence does not contribute to the delay between
+ offsets). Defaults to 'instant unitary'.
+
+ Returns
+ -------
+ cirq.Circuit
+ The circuit containing gates corresponding to sequence operation.
+
+ Raises
+ ------
+ ArgumentsValueError
+ If any of the input parameters result in an invalid operation.
+
+ Notes
+ -----
+
+ Dynamic Decoupling Sequences (DDS) consist of idealized pulse operation. Theoretically,
+ these operations (pi-pulses in X,Y or Z) occur instantaneously. However, in practice,
+ pulses require time. Therefore, this method of converting an idealized sequence
+ results to a circuit that is only an approximate implementation of the idealized sequence.
+
+ In idealized definition of DDS, `offsets` represents the instances within sequence
+ `duration` where a pulse occurs instantaneously. A series of appropriate circuit components
+ is placed in order to represent these pulses.
+
+ In 'standard circuit', the `gaps` or idle time in between active pulses are filled up
+ with `identity` gates. Each identity gate introduces a delay of `gate_time`. In this
+ implementation, the number of identity gates is determined by
+ :math:`np.int(np.floor(offset\\_distance / gate\\_time))`. As a consequence,
+ :math:`np.int(np.floor(offset\\_distance / gate\\_time))`. As a consequence,
+ the duration of the real-circuit is :math:`gate\\_time \\times number\\_of\\_identity\\_gates +
+ pulse\\_gate\\_time \\times number\\_of\\_pulses`.
+
+ Q-CTRL Cirq Adapter supports operation resulting in rotation around at most one axis at
+ any offset.
+ """
+
+ if dynamic_decoupling_sequence is None:
+ raise ArgumentsValueError('No dynamic decoupling sequence provided.',
+ {'dynamic_decoupling_sequence': dynamic_decoupling_sequence})
+
+ if not isinstance(dynamic_decoupling_sequence, DynamicDecouplingSequence):
+ raise ArgumentsValueError('Dynamical decoupling sequence is not recognized.'
+ 'Expected DynamicDecouplingSequence instance',
+ {'type(dynamic_decoupling_sequence)':
+ type(dynamic_decoupling_sequence)})
+
+ if gate_time <= 0:
+ raise ArgumentsValueError(
+ 'Time delay of gates must be greater than zero.',
+ {'gate_time': gate_time})
+
+ target_qubits = target_qubits or [cirq.LineQubit(0)]
+
+ if algorithm not in [FIX_DURATION_UNITARY, INSTANT_UNITARY]:
+ raise ArgumentsValueError('Algorithm must be one of {} or {}'.format(
+ INSTANT_UNITARY, FIX_DURATION_UNITARY), {'algorithm': algorithm})
+
+ unitary_time = 0.
+ if algorithm == FIX_DURATION_UNITARY:
+ unitary_time = gate_time
+
+ rabi_rotations = dynamic_decoupling_sequence.rabi_rotations
+ azimuthal_angles = dynamic_decoupling_sequence.azimuthal_angles
+ detuning_rotations = dynamic_decoupling_sequence.detuning_rotations
+
+ offsets = dynamic_decoupling_sequence.offsets
+
+ time_covered = 0
+ circuit = cirq.Circuit()
+ for offset, rabi_rotation, azimuthal_angle, detuning_rotation in zip(
+ list(offsets), list(rabi_rotations),
+ list(azimuthal_angles), list(detuning_rotations)):
+
+ offset_distance = offset - time_covered
+
+ if np.isclose(offset_distance, 0.0):
+ offset_distance = 0.0
+
+ if offset_distance < 0:
+ raise ArgumentsValueError(
+ "Offsets cannot be placed properly. Spacing between the rotations"
+ "is smaller than the time required to perform the rotation. Provide"
+ "a longer dynamic decoupling sequence or shorted gate time.",
+ {'dynamic_decoupling_sequence': dynamic_decoupling_sequence,
+ 'gate_time': gate_time})
+
+ while (time_covered+gate_time) <= offset:
+ gate_list = []
+ for qubit in target_qubits:
+ gate_list.append(cirq.I(qubit))
+ time_covered += gate_time
+ circuit.append(gate_list)
+
+ rotations = np.array([
+ rabi_rotation * np.cos(azimuthal_angle),
+ rabi_rotation * np.sin(azimuthal_angle),
+ detuning_rotation])
+
+ if np.sum(np.isclose(rotations, 0.0).astype(np.int)) == 1:
+ raise ArgumentsValueError(
+ 'Open Controls support a sequence with one '
+ 'valid rotation at any offset. Found a sequence '
+ 'with multiple rotation operations at an offset.',
+ {'dynamic_decoupling_sequence': dynamic_decoupling_sequence},
+ extras={'offset': offset,
+ 'rabi_rotation': rabi_rotation,
+ 'azimuthal_angle': azimuthal_angle,
+ 'detuning_rotation': detuning_rotation}
+ )
+
+ gate_list = []
+ for qubit in target_qubits:
+ if np.sum(np.isclose(rotations, 0.0).astype(np.int)) == 3:
+ gate_list.append(cirq.I(qubit))
+ else:
+ if not np.isclose(rotations[0], 0.0):
+ gate_list.append(cirq.Rx(rotations[0])(qubit))
+ elif not np.isclose(rotations[1], 0.0):
+ gate_list.append(cirq.Ry(rotations[1])(qubit))
+ elif not np.isclose(rotations[2], 0.):
+ gate_list.append(cirq.Rz(rotations[2])(qubit))
+ circuit.append(gate_list)
+
+ time_covered = offset + unitary_time
+
+ if add_measurement:
+ gate_list = []
+ for idx, qubit in enumerate(target_qubits):
+ gate_list.append(cirq.measure(qubit, key='qubit-{}'.format(idx)))
+ circuit.append(gate_list)
+
+ return circuit
diff --git a/qctrlcirq/schedule.py b/qctrlcirq/schedule.py
new file mode 100644
index 0000000..9a240c1
--- /dev/null
+++ b/qctrlcirq/schedule.py
@@ -0,0 +1,192 @@
+# Copyright 2019 Q-CTRL Pty Ltd & Q-CTRL Inc
+#
+# 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
+#
+# http://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.
+
+"""
+=============
+cirq.schedule
+=============
+"""
+
+import numpy as np
+
+import cirq
+
+from qctrlopencontrols import DynamicDecouplingSequence
+from qctrlopencontrols.exceptions.exceptions import ArgumentsValueError
+
+
+def convert_dds_to_cirq_schedule(
+ dynamic_decoupling_sequence,
+ target_qubits=None,
+ gate_time=0.1,
+ add_measurement=True,
+ device=None):
+ """Converts a Dynamic Decoupling Sequence into schedule
+ as defined in cirq
+
+ Parameters
+ ----------
+ dynamic_decoupling_sequence : DynamicDecouplingSequence
+ The dynamic decoupling sequence
+ target_qubits : list, optional
+ List of target qubits for the sequence operation; the qubits must be
+ cirq.Qid type; defaults to None in which case a 1-D lattice of one
+ qubit is used (indexed as 0).
+ gate_time : float, optional
+ Time (in seconds) delay introduced by a gate; defaults to 0.1
+ add_measurement : bool, optional
+ If True, the schedule contains a measurement operation for each of the
+ target qubits. Measurement from each of the qubits is associated
+ with a string as key. The string is formatted as 'qubit-X' where
+ X is a number between 0 and len(target_qubits).
+ device : cirq.Device, optional
+ A cirq.Device that specifies hardware constraints for validating operations.
+ If None, a unconstrained device is used. See `Cirq Documentation
+ ` _.
+
+ Returns
+ -------
+ cirq.Schedule
+ The schedule of sequence rotation operations.
+
+
+ Raises
+ ------
+ ArgumentsValueError
+ If any of the input parameters result in an invalid operation.
+
+ Notes
+ -----
+
+ Dynamic Decoupling Sequences (DDS) consist of idealized pulse operation. Theoretically,
+ these operations (pi-pulses in X,Y or Z) occur instantaneously. However, in practice,
+ pulses require time. Therefore, this method of converting an idealized sequence
+ results to a schedule that is only an approximate implementation of the idealized sequence.
+
+ In idealized definition of DDS, `offsets` represents the instances within sequence
+ `duration` where a pulse occurs instantaneously. A series of appropriate rotation
+ operations is placed in order to represent these pulses.
+
+ In cirq.schedule, the active pulses are scheduled to be activated at a certain
+ instant calculated from the start of the sequence and continues for a duration
+ of gate_time. This does not require identity gates to be placed between offsets.
+
+ Q-CTRL Cirq Adapter supports operation resulting in rotation around at most one axis at
+ any offset.
+ """
+
+ if dynamic_decoupling_sequence is None:
+ raise ArgumentsValueError('No dynamic decoupling sequence provided.',
+ {'dynamic_decoupling_sequence': dynamic_decoupling_sequence})
+
+ if not isinstance(dynamic_decoupling_sequence, DynamicDecouplingSequence):
+ raise ArgumentsValueError('Dynamical decoupling sequence is not recognized.'
+ 'Expected DynamicDecouplingSequence instance',
+ {'type(dynamic_decoupling_sequence)':
+ type(dynamic_decoupling_sequence)})
+
+ if gate_time <= 0:
+ raise ArgumentsValueError(
+ 'Time delay of gates must be greater than zero.',
+ {'gate_time': gate_time})
+
+ target_qubits = target_qubits or [cirq.LineQubit(0)]
+ device = device or cirq.devices.UNCONSTRAINED_DEVICE
+
+ if not isinstance(device, cirq.Device):
+ raise ArgumentsValueError('Device must be a cirq.Device type.',
+ {'device': device})
+
+ # time in nano seconds
+ gate_time = gate_time * 1e9
+
+ rabi_rotations = dynamic_decoupling_sequence.rabi_rotations
+ azimuthal_angles = dynamic_decoupling_sequence.azimuthal_angles
+ detuning_rotations = dynamic_decoupling_sequence.detuning_rotations
+
+ if len(rabi_rotations.shape) == 1:
+ rabi_rotations = rabi_rotations[np.newaxis, :]
+ if len(azimuthal_angles.shape) == 1:
+ azimuthal_angles = azimuthal_angles[np.newaxis, :]
+ if len(detuning_rotations.shape) == 1:
+ detuning_rotations = detuning_rotations[np.newaxis, :]
+
+ operations = np.vstack((rabi_rotations, azimuthal_angles, detuning_rotations))
+ offsets = dynamic_decoupling_sequence.offsets
+ # offsets in nano seconds
+ offsets = offsets * 1e9
+
+ scheduled_operations = []
+ offset_count = 0
+ for op_idx in range(operations.shape[1]):
+
+ rabi_rotation = dynamic_decoupling_sequence.rabi_rotations[offset_count]
+ azimuthal_angle = dynamic_decoupling_sequence.azimuthal_angles[offset_count]
+
+ rotations = np.array([
+ rabi_rotation * np.cos(azimuthal_angle),
+ rabi_rotation * np.sin(azimuthal_angle),
+ dynamic_decoupling_sequence.detuning_rotations[offset_count]])
+
+ if np.sum(np.isclose(rotations, 0.0).astype(np.int)) == 1:
+ raise ArgumentsValueError(
+ 'Open Controls support a sequence with one'
+ 'valid pulse at any offset. Found sequence'
+ 'with multiple rotation operations at an offset.',
+ {'dynamic_decoupling_sequence': str(dynamic_decoupling_sequence),
+ 'offset': dynamic_decoupling_sequence.offsets[op_idx],
+ 'rabi_rotation': dynamic_decoupling_sequence.rabi_rotations[
+ op_idx],
+ 'azimuthal_angle': dynamic_decoupling_sequence.azimuthal_angles[
+ op_idx],
+ 'detuning_rotaion': dynamic_decoupling_sequence.detuning_rotations[
+ op_idx]}
+ )
+
+ for qubit in target_qubits:
+ if np.sum(np.isclose(rotations, 0.0).astype(np.int)) == 0:
+ operation = cirq.ScheduledOperation(
+ time=cirq.Timestamp(nanos=offsets[op_idx]),
+ duration=cirq.Duration(nanos=gate_time),
+ operation=cirq.I(qubit))
+ else:
+ if not np.isclose(rotations[0], 0.0):
+ operation = cirq.ScheduledOperation(
+ time=cirq.Timestamp(nanos=offsets[op_idx]),
+ duration=cirq.Duration(nanos=gate_time),
+ operation=cirq.Rx(rotations[0])(qubit))
+ elif not np.isclose(rotations[1], 0.0):
+ operation = cirq.ScheduledOperation(
+ time=cirq.Timestamp(nanos=offsets[op_idx]),
+ duration=cirq.Duration(nanos=gate_time),
+ operation=cirq.Rx(rotations[1])(qubit))
+ elif not np.isclose(rotations[2], 0.):
+ operation = cirq.ScheduledOperation(
+ time=cirq.Timestamp(nanos=offsets[op_idx]),
+ duration=cirq.Duration(nanos=gate_time),
+ operation=cirq.Rx(rotations[2])(qubit))
+ offset_count += 1
+ scheduled_operations.append(operation)
+
+ if add_measurement:
+ for idx, qubit in enumerate(target_qubits):
+ operation = cirq.ScheduledOperation(
+ time=cirq.Timestamp(nanos=offsets[-1] + gate_time),
+ duration=cirq.Duration(nanos=gate_time),
+ operation=cirq.MeasurementGate(
+ 1, key='qubit-{}'.format(idx))(qubit))
+ scheduled_operations.append(operation)
+
+ schedule = cirq.Schedule(device=device, scheduled_operations=scheduled_operations)
+ return schedule
diff --git a/setup-poetry.sh b/setup-poetry.sh
new file mode 100755
index 0000000..19ab3b9
--- /dev/null
+++ b/setup-poetry.sh
@@ -0,0 +1,42 @@
+#!/bin/bash
+
+echo "--- Checking appropriate prerequisites are installed."
+
+# Check correct version of Python
+python -V | grep 3.7
+if [ $? -ne 0 ]
+then
+ echo "Please ensure you have Python 3.7 activated. Pyenv is recommended."
+ exit 1
+fi
+
+# Check Poetry installed
+poetry --version | grep 0.12
+if [ $? -ne 0 ]
+then
+ echo "--- Poetry not detected, installing..."
+ curl -sSL https://raw.githubusercontent.com/sdispater/poetry/master/get-poetry.py | python
+fi
+
+# Do dry run to create virtual environment
+echo "--- Using Poetry to create virtual environment"
+poetry install --dry-run -q
+
+# Activate created virtual environment
+echo "--- Activating virtual environment"
+VIRTUAL_ENV=$(poetry run python -c "import os; print(os.environ['VIRTUAL_ENV'])")
+echo "Virtual environment path is $VIRTUAL_ENV"
+source $VIRTUAL_ENV/bin/activate
+if [ $? -ne 0 ]
+then
+ echo "Could not activate the virtual environment!"
+ exit 2
+fi
+
+echo "--- Updating pip and setuptools"
+pip install -q --upgrade pip setuptools
+deactivate
+
+# Do the final install
+echo "--- Installing dependencies"
+poetry update
\ No newline at end of file
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..b866ade
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,40 @@
+
+# -*- coding: utf-8 -*-
+
+# DO NOT EDIT THIS FILE!
+# This file has been autogenerated by dephell <3
+# https://github.com/dephell/dephell
+
+try:
+ from setuptools import setup
+except ImportError:
+ from distutils.core import setup
+
+
+import os.path
+
+readme = ''
+here = os.path.abspath(os.path.dirname(__file__))
+readme_path = os.path.join(here, 'README.rst')
+if os.path.exists(readme_path):
+ with open(readme_path, 'rb') as stream:
+ readme = stream.read().decode('utf8')
+
+
+setup(
+ long_description=readme,
+ name='qctrl-cirq',
+ version='0.0.1rc5',
+ description='Q-CTRL Cirq Adapter',
+ python_requires='<3.8,>=3.6.4',
+ project_urls={'repository': 'https://github.com/qctrl/python-cirq'},
+ author='Q-CTRL',
+ author_email='support@q-ctrl.com',
+ license='Apache-2.0',
+ keywords='quantum computing open source engineering cirq',
+ classifiers=['Development Status :: 5 - Production/Stable', 'Environment :: Console', 'Intended Audience :: Developers', 'Intended Audience :: Education', 'Intended Audience :: Science/Research', 'License :: OSI Approved :: Apache Software License', 'Natural Language :: English', 'Operating System :: OS Independent', 'Programming Language :: Python :: 3.6', 'Topic :: Scientific/Engineering :: Physics', 'Topic :: Scientific/Engineering :: Visualization', 'Topic :: Software Development :: Embedded Systems', 'Topic :: System :: Distributed Computing'],
+ packages=['qctrlcirq'],
+ package_data={},
+ install_requires=['cirq==0.*,>=0.6.0', 'numpy==1.*,>=1.16.0', 'qctrl-open-controls==3.*,>=3.0.0', 'scipy==1.*,>=1.3.0', 'toml==0.*,>=0.10.0'],
+ extras_require={'dev': ['pylama', 'pylint', 'pylint-runner', 'pytest', 'sphinx==2.*,>=2.2.0']},
+)
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 0000000..a2b6149
--- /dev/null
+++ b/tests/__init__.py
@@ -0,0 +1,13 @@
+# Copyright 2019 Q-CTRL Pty Ltd & Q-CTRL Inc
+#
+# 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
+#
+# http://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.
diff --git a/tests/test_cirq_sequence.py b/tests/test_cirq_sequence.py
new file mode 100644
index 0000000..bd9e36e
--- /dev/null
+++ b/tests/test_cirq_sequence.py
@@ -0,0 +1,114 @@
+# Copyright 2019 Q-CTRL Pty Ltd & Q-CTRL Inc
+#
+# 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
+#
+# http://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.
+
+"""
+===================================
+Tests converstion to Cirq Circuit
+===================================
+"""
+
+import cirq
+
+from qctrlopencontrols import new_predefined_dds
+from qctrlcirq import (
+ convert_dds_to_cirq_circuit,
+ convert_dds_to_cirq_schedule)
+
+
+def _create_test_sequence(sequence_scheme, pre_post_rotation):
+
+ """Create a DD sequence of choice'''
+
+ Parameters
+ ----------
+ sequence_scheme : str
+ One of 'Spin echo', 'Carr-Purcell', 'Carr-Purcell-Meiboom-Gill',
+ 'Uhrig single-axis', 'Periodic single-axis', 'Walsh single-axis',
+ 'Quadratic', 'X concatenated',
+ 'XY concatenated'
+ pre_post_rotation : bool
+ If True, adds a :math:`X_{\\pi/2}` gate on either ends
+
+ Returns
+ -------
+ DynamicDecouplingSequence
+ The Dynamical Decoupling Sequence instance built from supplied
+ schema information
+ """
+
+ dd_sequence_params = dict()
+ dd_sequence_params['scheme'] = sequence_scheme
+ dd_sequence_params['duration'] = 4
+ dd_sequence_params['pre_post_rotation'] = pre_post_rotation
+
+ # 'spin_echo' does not need any additional parameter
+
+ if dd_sequence_params['scheme'] in ['Carr-Purcell', 'Carr-Purcell-Meiboom-Gill',
+ 'Uhrig single-axis', 'periodic single-axis']:
+
+ dd_sequence_params['number_of_offsets'] = 2
+
+ elif dd_sequence_params['scheme'] in ['Walsh single-axis']:
+
+ dd_sequence_params['paley_order'] = 5
+
+ elif dd_sequence_params['scheme'] in ['quadratic']:
+
+ dd_sequence_params['duration'] = 16
+ dd_sequence_params['number_outer_offsets'] = 4
+ dd_sequence_params['number_inner_offsets'] = 4
+
+ elif dd_sequence_params['scheme'] in ['X concatenated',
+ 'XY concatenated']:
+
+ dd_sequence_params['duration'] = 16
+ dd_sequence_params['concatenation_order'] = 2
+
+ sequence = new_predefined_dds(**dd_sequence_params)
+ return sequence
+
+
+def _check_circuit_output(pre_post_rotation, conversion_method,
+ expected_state):
+ """Check the outcome of a circuit against expected outcome
+ """
+
+ simulator = cirq.Simulator()
+ for sequence_scheme in ['Carr-Purcell', 'Carr-Purcell-Meiboom-Gill',
+ 'Uhrig single-axis', 'periodic single-axis',
+ 'Walsh single-axis', 'quadratic', 'X concatenated',
+ 'XY concatenated']:
+ sequence = _create_test_sequence(sequence_scheme, pre_post_rotation)
+ cirq_circuit = conversion_method(
+ dynamic_decoupling_sequence=sequence,
+ add_measurement=True)
+
+ results = simulator.run(cirq_circuit)
+ assert results.measurements['qubit-0'] == expected_state
+
+
+def test_cirq_circuit_operation():
+
+ """Tests if the Dynamic Decoupling Sequence gives rise to expected
+ state with different pre-post gates parameters in cirq circuits
+ """
+ _check_circuit_output(False, convert_dds_to_cirq_circuit, 0)
+ _check_circuit_output(True, convert_dds_to_cirq_circuit, 1)
+
+ _check_circuit_output(False, convert_dds_to_cirq_schedule, 0)
+ _check_circuit_output(True, convert_dds_to_cirq_schedule, 1)
+
+
+if __name__ == '__main__':
+ pass