microsoft/qdk

Public

mirrored from https://github.com/microsoft/qdkAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v1.25.1

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

source/pip/qsharp/_device/_atom/_reorder.py

114lines · modecode

1# Copyright (c) Microsoft Corporation.
2# Licensed under the MIT License.
3
4from ._utils import as_qis_gate, get_used_values, uses_any_value
5from .._device import Device
6from pyqir import (
7 Call,
8 Instruction,
9 Function,
10 QirModuleVisitor,
11)
12
13
14def is_output_recording(instr: Instruction):
15 if isinstance(instr, Call):
16 return instr.callee.name.endswith("_record_output")
17 return False
18
19
20def is_irreversible(instr: Instruction):
21 if isinstance(instr, Call) and isinstance(instr.callee, Function):
22 return "irreversible" in instr.callee.attributes.func
23 return False
24
25
26class Reorder(QirModuleVisitor):
27 """
28 Reorder instructions within a block to find contiguous sequences of the same gate on
29 different qubits. This enables both layout and scheduling at a later stage.
30 """
31
32 def __init__(self, device: Device):
33 super().__init__()
34 self.device = device
35
36 def instr_key(self, instr: Instruction):
37 gate = as_qis_gate(instr)
38 if gate != {}:
39 qubits = sorted(map(self.device.get_ordering, gate["qubit_args"]))
40 return qubits[0]
41 return 0
42
43 def _on_block(self, block):
44 # The instructions are collected into an ordered list of steps, where each step
45 # contains instructions of the same type that do not depend on each other.
46 steps = []
47
48 # A list of all values or resultsused in the current step. This is used to determine if an instruction
49 # can be added to the current step or if it needs to go into a new step by checking dependencies.
50 values_used_in_step = []
51 results_used_in_step = []
52
53 # Output recording instructions and terminator must be treated separately, as those
54 # must be at the end of the block.
55 output_recording = []
56 terminator = block.terminator
57 if terminator:
58 terminator.remove()
59
60 for instr in block.instructions:
61 # Remove the instruction from the block, which keeps it alive in the module
62 # and available for later insertion.
63 instr.remove()
64 if is_output_recording(instr):
65 # Gather output recording instructions to be placed at the end of the block just before
66 # the terminator.
67 output_recording.append(instr)
68 else:
69 # Find the last step that contains instructions that the current instruction
70 # depends on. We want to insert the current instruction on the earliest possible
71 # step without violating dependencies.
72 last_dependent_step_idx = len(steps) - 1
73 (used_values, used_results) = get_used_values(instr)
74 while last_dependent_step_idx >= 0:
75 if uses_any_value(
76 used_values, values_used_in_step[last_dependent_step_idx]
77 ) or uses_any_value(
78 used_results, results_used_in_step[last_dependent_step_idx]
79 ):
80 break
81 last_dependent_step_idx -= 1
82
83 if isinstance(instr, Call):
84 while (
85 last_dependent_step_idx < len(steps) - 1
86 and isinstance(steps[last_dependent_step_idx + 1][0], Call)
87 and instr.callee != steps[last_dependent_step_idx + 1][0].callee
88 ):
89 last_dependent_step_idx += 1
90
91 if last_dependent_step_idx == len(steps) - 1:
92 # The current instruction depends on the last step, so add it to a new step at the end.
93 steps.append([instr])
94 values_used_in_step.append(set(used_values))
95 results_used_in_step.append(set(used_results))
96 else:
97 # The last dependent step is before the end, so add the current instruction to the
98 # step after it.
99 steps[last_dependent_step_idx + 1].append(instr)
100 values_used_in_step[last_dependent_step_idx + 1].update(used_values)
101 results_used_in_step[last_dependent_step_idx + 1].update(
102 used_results
103 )
104
105 # Insert the instructions back into the block in the correct order.
106 self.builder.insert_at_end(block)
107 for step in steps:
108 for instr in sorted(step, key=self.instr_key):
109 self.builder.instr(instr)
110 # Add output recording instructions and terminator at the end of the block.
111 for instr in output_recording:
112 self.builder.instr(instr)
113 if terminator:
114 self.builder.instr(terminator)
115