Skip to content

Commit

Permalink
Merge pull request #15 from calculquebec/release
Browse files Browse the repository at this point in the history
Releasing version 0.3
  • Loading branch information
Scirelgar committed Aug 26, 2024
2 parents c0491d8 + d1ad9ed commit d04c7ef
Show file tree
Hide file tree
Showing 12 changed files with 265 additions and 175 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
*.vscode
.benchmarks/*
dist/*
docs/_build/*
docs/_build/*
build
54 changes: 27 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,35 +6,23 @@ The PennyLane-Snowflurry plugin provides a PennyLane device that allows the use

[Snowflurry](https://snowflurry.org/) is a quantum computing framework developed in Julia by Anyon Systems and aims to provide access to quantum hardware and simulators.

PennyLane-Snowflurry makes use of dependencies such as PyJulia and PyCall to allow interfacing between Python and Julia, and thus between PennyLane and Snowflurry.
PennyLane-Snowflurry makes use of dependencies like [PythonCall and JuliaCall](https://github.com/JuliaPy/PythonCall.jl) to allow interfacing between Python and Julia, and thus between PennyLane and Snowflurry.

## Project structure

As shown in the diagram below, this plugin is used in Pennylane as a [device](https://pennylane.ai/plugins/) named `snowflurry.qubit`. This device is defined by the class `SnowflurryQubitDevice`. It converts a PennyLane circuit into a Snowflurry circuit, thanks to packages like PyJulia that allow the communication between Python and Julia environments. The Snowflurry circuit can then be used with the available backends, either a simulator or real quantum hardware. The results are then converted back into PennyLane's format and returned to the user.
As shown in the diagram below, this plugin is used in Pennylane as a [device](https://pennylane.ai/plugins/) named `snowflurry.qubit`. This device is defined by the class `SnowflurryQubitDevice`. It converts a PennyLane circuit into a Snowflurry circuit, thanks to packages like JuliaCall that allow the communication between Python and Julia environments. The Snowflurry circuit can then be used with the available backends, either a simulator or real quantum hardware. The results are then converted back into PennyLane's format and returned to the user.

![interaction_diagram](https://raw.githubusercontent.com/calculquebec/pennylane-snowflurry/main/doc/interaction_diagram_extended.png)

## Local installation

Since this plugin interfaces between Python and Julia, it requires both languages to be installed on your machine. As Python is widely used amongst the quantum computing community, we assume you already have it installed. The rest of this section will guide you through the installation of Julia and the plugin.
Since this plugin interfaces between Python and Julia, it requires both languages to be installed on your machine. As Python is widely used amongst the quantum computing community, we assume you already have it installed with a package manager like pip.

### Julia

If you don't have Julia installed, you can download it from the [official website](https://julialang.org/downloads/). It is highly recommended to install using the installer file, as it will ask to add Julia to the system's environment variables.

**To ensure this correct configuration, during the installation process, the checkbox `Add Julia to PATH` must be checked.**

### PennyLane and Snowflurry

Before installing this plugin, makes sure you have a working Pennylane and Snowflurry installation.

For PennyLane, please refer to the [PennyLane documentation](https://pennylane.ai/install/).

For Snowflurry, please refer to the [Snowflurry documentation](https://snowflurry.org).
Note that to use [Calcul Québec's services](https://docs.alliancecan.ca/wiki/Les_services_quantiques/en), you may not need to install the plugin locally as our users might have access to a pre-configured environment.

### Plugin installation

This plugin is available on PyPI, so you can install it with pip:
Pennylane-snowflurry can be installed using pip:

```sh
pip install pennylane-snowflurry
Expand All @@ -46,21 +34,33 @@ Alternatively, you can clone this repo and install the plugin with the following
pip install -e .
```

### PyJulia and PyCall
Pennylane and other Python dependencies will be installed automatically during the installation process.

PyJulia and PyCall are used to communicate between Python and Julia. At this point, PyJulia should already be installed with the plugin as it is listed in the dependencies, but you'll need to install PyCall in your Julia environment. To do so open a python terminal and execute the following commands:
The plugin will also take care of installing Julia and the required Julia packages, such as Snowflurry, PythonCall during the first run. Some notes on that matter are provided below.

If you wish to disable this behaviour, you can edit the julia_env.py file and set the value of the variable `IS_USER_CONFIGURED` to `TRUE`:

```py
import julia
julia.install()
IS_USER_CONFIGURED = True
```

Alternatively, you could also install PyCall from the Julia REPL, but the previous method makes sure to build the package for your current python environment.
## Julia

```julia
using Pkg
Pkg.add("PyCall")
```
As of version 0.3.0, **there is no need to install Julia manually**, since the plugin will download and install the required version automatically upon first use. This Julia environment is bound to the plugin.

However, if you wish to manage your Julia environment, you can download it from the [official website](https://julialang.org/downloads/). It is highly recommended to install using the installer file, as it will ask to add Julia to the system's environment variables.

**To ensure this correct configuration, during the installation process, the checkbox `Add Julia to PATH` must be checked.**

## PennyLane and Snowflurry

Those packages are installed automatically during the plugin installation process and are necessary for the plugin to work. Here are the links to their respective documentation:

For PennyLane, please refer to the [PennyLane documentation](https://pennylane.ai/install/).

For Snowflurry, please refer to the [Snowflurry documentation](https://snowflurry.org).

## Usage

### Running files

Expand All @@ -70,7 +70,7 @@ The plugin can be used both in python scripts and Jupyter notebooks. To run a sc
python base_circuit.py
```

## Usage
### How to call the device

Once installed, you can write your PennyLane circuits as usual, but you'll need to specify the device as `snowflurry.qubit` and provide the Snowflurry backend you want to use if you have access to a quantum computer.

Expand Down
Binary file modified doc/interaction_diagram_extended.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions pennylane_snowflurry/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
from .julia_setup import JuliaEnv

JuliaEnv().update()

from .pennylane_converter import PennylaneConverter
from .snowflurry_device import SnowflurryQubitDevice
2 changes: 1 addition & 1 deletion pennylane_snowflurry/_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@
"""


__version__ = "0.2.1"
__version__ = "0.3.0"
112 changes: 112 additions & 0 deletions pennylane_snowflurry/julia_setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import juliapkg
from juliapkg import PkgSpec
import json
import os

# Required packages for the Julia environment
REQUIRED_PACKAGES = [
PkgSpec(
name="Snowflurry", uuid="7bd9edc1-4fdc-40a1-a0f6-da58fb4f45ec", version="0.3"
),
]

IS_USER_CONFIGURED = False


class JuliaEnv:
"""
This class is used to resolve dependencies for the Julia environment. It will install Julia and the required
packages if they are not already installed or up to date.
It makes use of the juliapkg Python package to manage installations and dependencies. Upon initialization,
juliapkg will create a directory in which metadata regarding dependencies will be stored. A JSON file saved in
this directory is used by juliapkg to resolve dependencies.
Simply calling the update method will install the required packages by manipulating the JSON file and forcing
juliapkg to resolve dependencies.
It is possible to deactivate the automatic update by setting the IS_USER_CONFIGURED variable to True.
Attributes:
julia_env_path: str: The path to the Julia environment metadata directory created by juliapkg
json_pkg_list: dict: Content of the JSON file used by juliapkg to resolve dependencies
json_path: str: The path to the juliapkg.json file used by juliapkg to resolve dependencies
required_packages: list: The required packages defined in the REQUIRED_PACKAGES variable in julia_setup.py
"""

def __init__(self):
self.julia_env_path = juliapkg.project()
self.json_path = self.julia_env_path + "/pyjuliapkg/juliapkg.json"
self.json_pkg_list = self.get_json_pkg_list()
self.required_packages = REQUIRED_PACKAGES

def update(self):
"""
This function updates the Julia environment by manipulating a JSON file and forcing juliapkg to resolve
the environment's dependencies.
Setting the IS_USER_CONFIGURED variable to True will deactivate the update.
"""
if IS_USER_CONFIGURED:
return

for required_pkg in self.required_packages:
if required_pkg.name in self.json_pkg_list:
if self.parse_version(required_pkg):
continue
self.new_json_pkg_list()
self.write_json()
juliapkg.resolve(force=True)
break

def get_json_pkg_list(self) -> dict:
"""
This function returns a dictionary of packages in the configuration file juliapkg.json.
The key is the package name and the value is a dictionary containing the package's UUID and version.
Returns: dict
"""
if not os.path.exists(self.json_path):
return {}
with open(self.json_path, "r") as f:
juliapkg_data = json.load(f)

return juliapkg_data.get("packages", [])

def parse_version(self, required_pkg) -> bool:
"""
This function compares the version of the required package with the version in the JSON file.
Args:
required_pkg: PkgSpec: The required package
"""
try:
for package_name, package_info in self.json_pkg_list.items():
if str(package_name) == required_pkg.name:
if package_info.get("version", []) == required_pkg.version:
return True
return False
except:
LookupError("Package not found")

def write_json(self):
"""
This function writes the updated list of packages to the JSON file
It also creates the JSON file if it does not exist.
"""
if "packages" not in self.json_pkg_list:
self.json_pkg_list = {"packages": self.json_pkg_list}
with open(self.json_path, "w") as f:
json.dump(self.json_pkg_list, f)

def new_json_pkg_list(self):
"""
This function updates the list of packages with the required packages
"""

for required_pkg in self.required_packages:
self.json_pkg_list[required_pkg.name] = {
"uuid": required_pkg.uuid,
"version": required_pkg.version,
}
Loading

0 comments on commit d04c7ef

Please sign in to comment.