Skip to content

Commit

Permalink
Merge pull request #169 from QuTech-Delft/v3-mvp-python
Browse files Browse the repository at this point in the history
Enable v3 MVP to be used from Python
  • Loading branch information
rturrado authored Dec 5, 2023
2 parents 52340ed + 8581beb commit 726ab59
Show file tree
Hide file tree
Showing 51 changed files with 432 additions and 209 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ jobs:
with:
python-version: "3.11"
- name: Install Python dependencies
run: python -m pip install --upgrade pip numpy pytest setuptools wheel
run: python -m pip install --upgrade pip conan numpy pytest setuptools wheel
- name: Install SWIG, and set build type (Linux)
if: matrix.os == 'ubuntu-latest'
run: |
Expand Down
5 changes: 4 additions & 1 deletion include/v10/libQasm.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@

class libQasm {
public:
libQasm() : parse_result_{ 0 }, qasm_rep_{} {}
libQasm()
: qasm_rep_{}
, parse_result_{ 0 }
{}

void parse_string(const std::string &qasm_str) {
compiler::QasmSemanticChecker sm(qasm_str);
Expand Down
4 changes: 2 additions & 2 deletions include/v1x/cqasm-analyzer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -310,13 +310,13 @@ class Analyzer {
/**
* Parses and analyzes the given file.
*/
AnalysisResult analyze(const std::string &filename) const;
AnalysisResult analyze_file(const std::string &filename) const;

/**
* Parses and analyzes the given file pointer.
* The optional filename argument will be used only for error messages.
*/
AnalysisResult analyze(FILE *file, const std::string &filename = "<unknown>") const;
AnalysisResult analyze_file(FILE *file, const std::string &filename = "<unknown>") const;

/**
* Parses and analyzes the given string.
Expand Down
18 changes: 13 additions & 5 deletions include/v1x/cqasm-py.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <string>
#include <vector>


// Forward declarations for internal types.
namespace cqasm::v1x::analyzer { class Analyzer; }

Expand All @@ -23,7 +24,7 @@ class V1xAnalyzer {
/**
* Reference to the actual C++ analyzer that this wraps.
*/
std::unique_ptr<cqasm::v1x::analyzer::Analyzer> a;
std::unique_ptr<cqasm::v1x::analyzer::Analyzer> analyzer;

public:
/**
Expand All @@ -41,6 +42,13 @@ class V1xAnalyzer {
bool without_defaults = false
);

/**
* std::unique_ptr<T> requires T to be a complete class for the ~T operation.
* Since we are using a forward declaration for Analyzer, we need to declare ~T in the header file,
* and implement it in the source file.
*/
~V1xAnalyzer();

/**
* Registers an instruction type.
* The arguments are passed straight to instruction::Instruction's constructor.
Expand All @@ -67,9 +75,9 @@ class V1xAnalyzer {
* Only parses the given file.
* The file must be in v1.x syntax.
* No version check or conversion is performed.
* Returns a vector of strings,
* of which the first is always present and is the CBOR serialization of the v1.x AST.
* Returns a vector of strings, of which the first is reserved for the CBOR serialization of the v1.x AST.
* Any additional strings represent error messages.
* Notice that the AST and error messages won't be available at the same time.
*/
static std::vector<std::string> parse_file(
const std::string &filename
Expand All @@ -89,9 +97,9 @@ class V1xAnalyzer {
* If the file is written in a later file version,
* this function may try to reduce it to the maximum v1.x API version support advertised
* using this object's constructor.
* Returns a vector of strings,
* of which the first is always present and is the CBOR serialization of the v1.x semantic tree.
* Returns a vector of strings, of which the first is reserved for the CBOR serialization of the v1.x semantic tree.
* Any additional strings represent error messages.
* Notice that the AST and error messages won't be available at the same time.
*/
std::vector<std::string> analyze_file(
const std::string &filename
Expand Down
4 changes: 2 additions & 2 deletions include/v1x/cqasm.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ namespace v1x {
* Parses and analyzes the given file path with the default analyzer,
* dumping error messages to stderr and throwing an analyzer::AnalysisFailed on failure.
*/
tree::One<semantic::Program> analyze(
tree::One<semantic::Program> analyze_file(
const std::string &file_path,
const std::string &api_version = "1.0"
);
Expand All @@ -35,7 +35,7 @@ tree::One<semantic::Program> analyze(
* error messages to stderr and throwing an analyzer::AnalysisFailed on failure.
* The optional file_name is only used for error messages.
*/
tree::One<semantic::Program> analyze(
tree::One<semantic::Program> analyze_file(
FILE *fp,
const std::string &file_name = "<unknown>",
const std::string &api_version = "1.0"
Expand Down
7 changes: 3 additions & 4 deletions include/v3x/BuildCustomAstVisitor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,12 @@
#include "cqasm-ast.hpp"
#include "v3x/CqasmParser.h"
#include "v3x/CqasmParserVisitor.h"
#include "v3x/CustomErrorListener.hpp"

#include <antlr4-runtime.h> // Token


#include <any>

namespace antlr4 { class Token; }
namespace cqasm::v3x::parser { class CustomErrorListener; }


namespace cqasm::v3x::parser {

Expand Down
4 changes: 2 additions & 2 deletions include/v3x/BuildTreeGenAstVisitor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
#include "v3x/BuildCustomAstVisitor.hpp"
#include "v3x/CqasmParser.h"
#include "v3x/CqasmParserVisitor.h"
#include "v3x/CustomErrorListener.hpp"

#include <any>

namespace cqasm::v3x::parser { class CustomErrorListener; }


namespace cqasm::v3x::parser {

Expand Down
5 changes: 4 additions & 1 deletion include/v3x/ScannerAntlr.hpp
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
#pragma once

#include "v3x/BuildCustomAstVisitor.hpp"
#include "v3x/CustomErrorListener.hpp"
#include "v3x/cqasm-parse-result.hpp"

#include <memory> // unique_ptr
#include <string>

namespace antlr4 { class ANTLRInputStream; }
namespace cqasm::v3x::parser { class CustomErrorListener; }


namespace cqasm::v3x::parser {

Expand Down
2 changes: 1 addition & 1 deletion include/v3x/cqasm-analyzer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ class Analyzer {
/**
* Parses and analyzes the given file.
*/
[[nodiscard]] AnalysisResult analyze(const std::string &filename);
[[nodiscard]] AnalysisResult analyze_file(const std::string &filename);

/**
* Parses and analyzes the given string.
Expand Down
1 change: 0 additions & 1 deletion include/v3x/cqasm-parse-helper.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
#include "v3x/cqasm-parse-result.hpp"
#include "v3x/ScannerAntlr.hpp"

#include <antlr4-runtime.h>
#include <memory> // unique_ptr
#include <string>

Expand Down
22 changes: 15 additions & 7 deletions include/v3x/cqasm-py.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <string>
#include <vector>


// Forward declarations for internal types.
namespace cqasm::v3x::analyzer { class Analyzer; }

Expand All @@ -23,7 +24,7 @@ class V3xAnalyzer {
/**
* Reference to the actual C++ analyzer that this wraps.
*/
std::unique_ptr<cqasm::v3x::analyzer::Analyzer> a;
std::unique_ptr<cqasm::v3x::analyzer::Analyzer> analyzer;

public:
/**
Expand All @@ -36,11 +37,18 @@ class V3xAnalyzer {
* the initial mappings and functions are not configurable at all.
* The defaults for these are always used.
*/
V3xAnalyzer(
explicit V3xAnalyzer(
const std::string &max_version = "3.0",
bool without_defaults=false
bool without_defaults = false
);

/**
* std::unique_ptr<T> requires T to be a complete class for the ~T operation.
* Since we are using a forward declaration for Analyzer, we need to declare ~T in the header file,
* and implement it in the source file.
*/
~V3xAnalyzer();

/**
* Registers an instruction type.
* The arguments are passed straight to instruction::Instruction's constructor.
Expand All @@ -51,9 +59,9 @@ class V3xAnalyzer {
* Only parses the given file.
* The file must be in v3.x syntax.
* No version check or conversion is performed.
* Returns a vector of strings,
* of which the first is always present and is the CBOR serialization of the v3.x AST.
* Returns a vector of strings, of which the first is reserved for the CBOR serialization of the v3.x AST.
* Any additional strings represent error messages.
* Notice that the AST and error messages won't be available at the same time.
*/
static std::vector<std::string> parse_file(
const std::string &filename
Expand All @@ -73,9 +81,9 @@ class V3xAnalyzer {
* If the file is written in a later file version,
* this function may try to reduce it to the maximum v3.x API version support advertised
* using this object's constructor.
* Returns a vector of strings,
* of which the first is always present and is the CBOR serialization of the v3.x semantic tree.
* Returns a vector of strings, of which the first is reserved for the CBOR serialization of the v3.x semantic tree.
* Any additional strings represent error messages.
* Notice that the AST and error messages won't be available at the same time.
*/
std::vector<std::string> analyze_file(
const std::string &filename
Expand Down
2 changes: 1 addition & 1 deletion include/v3x/cqasm.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ namespace v3x {
* Parses and analyzes the given file path with the default analyzer,
* dumping error messages to stderr and throwing an analyzer::AnalysisFailed on failure.
*/
tree::One<cqasm::v3x::semantic::Program> analyze(
tree::One<cqasm::v3x::semantic::Program> analyze_file(
const std::string &file_path,
const std::string &api_version = "3.0"
);
Expand Down
4 changes: 0 additions & 4 deletions pyproject.toml

This file was deleted.

5 changes: 5 additions & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[pytest]
testpaths =
"test/v10/python"
"test/v3x/python"
python_files = test_*.py
4 changes: 3 additions & 1 deletion python/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,9 @@ set(CMAKE_CXX_EXTENSIONS OFF)
#=============================================================================#

# Configure SWIG.
set(SWIG_FILE "${CMAKE_CURRENT_SOURCE_DIR}/libQasm.i")
set(SWIG_FILE
"${CMAKE_CURRENT_SOURCE_DIR}/libQasm.i"
)

set_source_files_properties("${SWIG_FILE}" PROPERTIES
CPLUSPLUS ON
Expand Down
29 changes: 14 additions & 15 deletions python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,21 @@ aside from the `setup.py` file living in the root directory.

File/directory list:

- `libQasm.i`: SWIG directives for the bindings.
- `CMakeLists.txt`: SWIG and the Python extension build are handled by CMake,
so it can make everything's linked together properly.
Don't use this file directly.
It's intended to be added by the root `CMakeLists.txt`,
and only when the build is orchestrated by `setup.py`.

- `libQasm`: represents the Python module excluding the extension. Any Python
files in here will be included in the Python install.
- `conda`: contains the conda recipe for libqasm,
in case you're using that rather than pip to manage your Python packages.
It mostly just defers to `setup.py`,
but will help you set up the build dependencies regardless of your platform.

- `conda`: contains the conda recipe for libqasm, in case you're using that
rather than pip to manage your Python packages. It mostly just defers to
`setup.py`, but will help you set up the build dependencies regardless of
your platform.
- `libQasm`: represents the Python module excluding the extension.
Any Python files in here will be included in the Python install.

- `CMakeLists.txt`: SWIG and the Python extension build are handled by CMake,
so it can make everything's linked together properly. Don't use this file
directly; it's intended to be added by the root `CMakeLists.txt`, and only
when the build is orchestrated by `setup.py`.

- `python_lib.py`: helper script for CMake to find the Python library to link
against, because FindPYTHON often picks the wrong one, especially in venvs.
- `libQasm.i`: SWIG directives for the bindings.

- `compat`: helper scripts for CMake to make old CMake versions work.
- `python_lib.py`: helper script for CMake to find the Python library to link against,
because FindPYTHON often picks the wrong one, especially in virtual environments.
2 changes: 2 additions & 0 deletions python/libQasm.i
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "v10/qasm_ast.hpp"
#include "v10/libQasm.hpp"
#include "v1x/cqasm-py.hpp"
#include "v3x/cqasm-py.hpp"
%}

%include "exception.i"
Expand Down Expand Up @@ -49,6 +50,7 @@ namespace std {
%include "v10/qasm_ast.hpp"
%include "v10/libQasm.hpp"
%include "v1x/cqasm-py.hpp"
%include "v3x/cqasm-py.hpp"

namespace std {
%template(subcircuit_vector) vector<shared_ptr<compiler::SubCircuit>>;
Expand Down
48 changes: 30 additions & 18 deletions python/module/cqasm/v1x/__init__.py
Original file line number Diff line number Diff line change
@@ -1,34 +1,46 @@
import libQasm;
import libQasm


class Analyzer(libQasm.V1xAnalyzer):

@staticmethod
def parse_file(*args):
retval = libQasm.V1xAnalyzer.parse_file(*args)
if len(retval) == 1:
ret = libQasm.V1xAnalyzer.parse_file(*args)
if len(ret) == 1:
import cqasm.v1x.ast as ast
return ast.Root.deserialize(retval[0].encode("utf-8", errors="surrogateescape"))
return list(retval[1:])
serialized_ast_str = str(ret[0])
serialized_ast_bytes = serialized_ast_str.encode(encoding='utf-8', errors="surrogateescape")
deserialized_ast = ast.Root.deserialize(serialized_ast_bytes)
return deserialized_ast
return [str(error) for error in ret[1:]]

@staticmethod
def parse_string(*args):
retval = libQasm.V1xAnalyzer.parse_string(*args)
if len(retval) == 1:
ret = libQasm.V1xAnalyzer.parse_string(*args)
if len(ret) == 1:
import cqasm.v1x.ast as ast
return ast.Root.deserialize(retval[0].encode("utf-8", errors="surrogateescape"))
return list(retval[1:])
serialized_ast_str = str(ret[0])
serialized_ast_bytes = serialized_ast_str.encode(encoding='utf-8', errors="surrogateescape")
deserialized_ast = ast.Root.deserialize(serialized_ast_bytes)
return deserialized_ast
return [str(error) for error in ret[1:]]

def analyze_file(self, *args):
retval = super().analyze_file(*args)
if len(retval) == 1:
ret = super().analyze_file(*args)
if len(ret) == 1:
import cqasm.v1x.semantic as semantic
print(retval[0].encode("utf-8", errors="surrogateescape"))
return semantic.Program.deserialize(retval[0].encode("utf-8", errors="surrogateescape"))
return list(retval[1:])
serialized_ast_str = str(ret[0])
serialized_ast_bytes = serialized_ast_str.encode(encoding='utf-8', errors="surrogateescape")
deserialized_ast = semantic.Root.deserialize(serialized_ast_bytes)
return deserialized_ast
return [str(error) for error in ret[1:]]

def analyze_string(self, *args):
retval = super().analyze_string(*args)
if len(retval) == 1:
ret = super().analyze_string(*args)
if len(ret) == 1:
import cqasm.v1x.semantic as semantic
return semantic.Program.deserialize(retval[0].encode("utf-8", errors="surrogateescape"))
return list(retval[1:])
serialized_ast_str = str(ret[0])
serialized_ast_bytes = serialized_ast_str.encode(encoding='utf-8', errors="surrogateescape")
deserialized_ast = semantic.Root.deserialize(serialized_ast_bytes)
return deserialized_ast
return [str(error) for error in ret[1:]]
Loading

0 comments on commit 726ab59

Please sign in to comment.