Upgrading PyQIR¶
PyQIR 0.8¶
PyQIR 0.7 was the last version of PyQIR to support QIR evaluation. Simulation of QIR is now available via the qir-runner
sparse simulator.
PyQIR 0.7¶
Packages¶
PyQIR 0.6 was the last version of PyQIR to use three packages (pyqir-evaluator
, pyqir-generator
, and pyqir-parser
) and a metapackage (pyqir
).
PyQIR 0.7 instead uses only a single package (pyqir
) that has the functionality of all previous packages.
If you imported the pyqir.generator
or pyqir.parser
modules, then the same or an equivalent API is available in the pyqir
module.
If you imported the pyqir.evaluator
module, it is still available under the same name with no API changes.
Generator¶
IR and bitcode conversion¶
The functions bitcode_to_ir
and ir_to_bitcode
were removed because the new Module
class has the same functionality.
Module
supports both parsing and generating QIR.
For example, instead of:
from pyqir.generator import bitcode_to_ir, ir_to_bitcode
ir = bitcode_to_ir(bitcode, "module_name")
bitcode = ir_to_bitcode(ir, "module_name")
# Or with a source filename:
ir = bitcode_to_ir(bitcode, "module_name", "source_filename")
Use this:
from pyqir import Context, Module
ir = str(Module.from_bitcode(Context(), bitcode, "name"))
bitcode = Module.from_ir(Context(), ir, "name").bitcode
# Or with a source filename:
m = Module.from_bitcode(Context(), bitcode, "module_name")
m.source_filename = "source_filename"
ir = str(m)
Types¶
If you generated programs with externally-linked functions, then you used the pyqir.generator.types
module to describe the type of the functions.
This module has been removed.
Types need to be created differently because they now directly contain LLVM type objects, which require an LLVM context.
PyQIR 0.6 |
PyQIR 0.7 |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
There are two ways to get a context
object:
Use the
context
property onSimpleModule
. For example:from pyqir import SimpleModule, Type module = SimpleModule("name", num_qubits=1, num_results=1) void = Type.void(module.context)
Create one yourself. But you also need to give the context you created to
SimpleModule
. For example:from pyqir import Context, SimpleModule, Type context = Context() module = SimpleModule("name", num_qubits=1, num_results=1, context=context) void = Type.void(context)
Parser¶
PyQIR 0.7 unified parsing and code generation into a single API that is designed to support both.
This makes PyQIR much more powerful and will enable workflows that involve inspecting, running passes on, or otherwise transforming QIR that is parsed or generated using PyQIR.
This means that the API for pyqir-parser
had to be completely redesigned, which unfortunately makes upgrading challenging.
Here are some tips.
Modules¶
Use Module.from_bitcode
or Module.from_ir
instead of the QirModule
constructor.
See IR and bitcode conversion.
Entry points and interop-friendly functions¶
Instead of QirModule.entrypoint_funcs
, QirModule.interop_funcs
, or QirModule.get_funcs_by_attr
, filter the Module.functions
list instead.
For example:
entry_point = next(filter(pyqir.is_entry_point, module.functions))
interops = filter(pyqir.is_interop_friendly, module.functions)
Instructions¶
The instruction class hierarchy was trimmed down significantly.
Most subclasses of QirInstr
were removed.
The surviving subclasses are Call
, FCmp
, ICmp
, Phi
and Switch
.
For any other instruction, it should be possible to use the base Instruction
class to do anything that was previously possible.
Use the opcode
property to check what kind of instruction it is, and the operands
property to read all of the values that the instruction references.
The successors
property is a subset of operands
and contains just the values that are basic blocks, which can be useful to follow control flow with br
instructions.
For example, if you have a terminator instruction term
, then you can get the instructions of its first successor with term.successors[0].instructions
.
Qubit and result IDs¶
In PyQIR 0.6, QirQubitConstant
and QirResultConstant
were subclasses of QirOperand
.
Instead, you can try to extract a static qubit or result ID from any value using pyqir.qubit_id(value)
and pyqir.result_id(value)
.
If the value isn’t the right kind, it will return None
.
Strings¶
In PyQIR 0.6, you could read a global string constant from a string value using QirModule.get_global_bytes_value(value)
.
This is possible now using pyqir.extract_byte_string(value)
.
Examples¶
To see examples of how the new parser API can be used, take a look at test_parser.py. You can also compare it with test_parser_api.py from PyQIR 0.6 for a before-and-after view of the same test cases using both the old and new APIs.