-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add CQCHandler abstract class, make CQCConnection subclass of it, add CQCToFile class for writing commands to file #39
Add CQCHandler abstract class, make CQCConnection subclass of it, add CQCToFile class for writing commands to file #39
Conversation
cqc/pythonLib.py
Outdated
self.pending_messages = [] | ||
|
||
# Set an app ID | ||
self._appID = 6 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why six? I think the functionality for increasing the appID could be moved to the abstract class
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was just a number that made it easier to recognize in the printed out commands. There is no real reason for it.
Hi @shyadow! Nice work :) I think it would be better as we talked about to instead have subclasses implement the sending and reaceiving of messages. In this way most things can go the the abstract class, such as |
@AckslD. So you mean
and I like the idea. I only wonder if there is a better name than send? |
@shyadow Yes, exactly. There might be a better name, |
@AckslD Alright, I'll work on implementing that. |
@AckslD I feel like you can generally say "I'm committing this message" and it can then mean committing to send it or committing to write it depending on what you are doing. |
@AckslD The construct commands have been merged. Also noticed sendCommand and sendCmdXtra could then be merged too, so those have been replaced by commit_command(). |
@shyadow Let me know when you want me to take another look |
@AckslD It should be good now. |
- Add CQCHandler abstract class - Make CQCConnection a subclass of CQCHandler - Add CQCWriteToFile class, which is a subclass of CQCHandler - Add tests for CQCWriteToFile
30c3137
to
2e62090
Compare
Note: In Develop, when creating a new qubit it send CQC Header and Command Header seperately. Here it sends them together. So a test needed to be changed to reflect that.
@AckslD I brought this pull request up to date with the |
@lgvacanti There are now tests and the docs are updated with the changes Victor did. So you can know rebase on |
@AckslD It should be good to start reviewing now. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work @lgvacanti! I had a few comments, see below
another file with the same name, but with 'binary' appended, which will contain | ||
the commands in binary form (which is easier to process later). | ||
|
||
:meth:`cqc.pythonLib.CQCToFile` does not support sending or receiving |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are there other commands not supported, like get-creation-time etc?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added sendGetTime(), allocate_qubits(), sendFactory(), create_qubits(),
tomography(), test_preparation() to the list.
There are some more methods in CQCConnection which are not available in CQCToFile, but they are things that usually only get used internally, I think, such as startClassicalServer() and some others.
with open(filename) as f: | ||
contents = f.read() | ||
print(contents) | ||
assert contents[6:10] == "\\x00" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be better if you instanciate the headers here (from cqcHeader
) and check the command w.r.t the variable in the same file. Otherwise, if we cange the headers we also need to change these tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe with these tests it would be better to just use and read the binary files. In that case it's much easier to extract the numbers. With the text file it would be a bit more complicated to translate "\x00" to a number of to have to struct.pack()
the number, convert it to text and then compare. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lgvacanti I'm not sure I fully understand. I think it would still be good to instanciate the headers using cqcHeader
cqc/pythonLib.py
Outdated
@@ -380,7 +623,19 @@ def close(self, release_qubits=True): | |||
self.flush() | |||
|
|||
if release_qubits: | |||
self.release_all_qubits() | |||
if len(self.active_qubits[:]) != 0: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not call release_all_qubits
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The sequence with release_all_qubits
went like this:
def close(self, release_qubits=True):
"""Handle closing actions.
Flushes remaining headers, releases all qubits, closes the
connections, and removes the app ID from the used app IDs.
"""
# Flush all remaining commands
if self._pending_headers:
self.flush()
if release_qubits:
self.release_all_qubits()
self._s.close()
self._pop_app_id()
self.closeClassicalServer()
for name in list(self._classicalConn):
self.closeClassicalChannel(name)
def release_all_qubits(self):
"""
Releases all qubits off this connection
"""
return self.release_qubits(self.active_qubits[:])
def release_qubits(self, qubits, notify=True, block=True, action=False):
"""
Release qubits so backend can free them up for other uses
:param qubits: a list of qubits to be released
:param notify: Do we wish to be notified when done.
:param block: Do we want the qubit to be blocked
:param action: Execute the releases recursively or sequencely
"""
if isinstance(qubits, qubit):
qubits = [qubits]
assert isinstance(qubits, list)
n = len(qubits)
if n == 0: # then we don't need to do anything
return
logging.debug("App {} tells CQC: Release {} qubits".format(self.name, n))
if action:
hdr_length = CQCCmdHeader.HDR_LENGTH + CQCSequenceHeader.HDR_LENGTH
else:
hdr_length = CQCCmdHeader.HDR_LENGTH
hdr = CQCHeader()
hdr.setVals(CQC_VERSION, CQC_TP_COMMAND, self._appID, hdr_length * n)
cqc_msg = hdr.pack()
release_messages = b""
for i in range(n):
q = qubits[i]
try:
q.check_active()
except QubitNotActiveError as e:
raise QubitNotActiveError(
str(e) + ". Qubit {} is not active. None of the qubits are released".format(q._qID)
)
q._set_active(False)
cmd_hdr = CQCCmdHeader()
cmd_hdr.setVals(q._qID, CQC_CMD_RELEASE, int(notify), int(block), int(action))
release_messages += cmd_hdr.pack()
if action:
seq_hdr = CQCSequenceHeader()
# After this one we are sending n-i-1 more releases
seq_hdr.setVals(hdr_length * (n - i - 1))
release_messages += seq_hdr.pack()
self._s.send(cqc_msg + release_messages)
if notify:
msg = self.readMessage()
self.check_error(msg[0])
if msg[0].tp != CQC_TP_DONE:
raise CQCUnsuppError(
"Unexpected message sent back from the server. Message: {}".format(msg[0].printable())
)
self.print_CQC_msg(msg)
So release_all_qubits()
isn't really needed, since you might as well just do release_qubits(self.active_qubits[:])
release_qubits()
has been replaced by construct_release()
, commit()
, and the if notify:
block. This fits better in the model of first constructing a message and then sending it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok!
self.commit(msg) | ||
|
||
@abstractmethod | ||
def new_qubitID(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Both here and in the subclass it is not fully clear what this method is supposed to do. I think it would be good to have a bit more explanation in the doc-string.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've updated them. See this commit 0a292d2
@@ -218,6 +467,9 @@ def __init__(self, name, socket_address=None, appID=None, pend_messages=False, | |||
:param network_name: None or str | |||
Used if simulaqron is used to load socket addresses for the backend | |||
""" | |||
|
|||
super().__init__(pend_messages=pend_messages) | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The setting of self._opened_with_with
below should probably move to the abstract class since it it checked when creating a qubit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done 89842ae
cqc/pythonLib.py
Outdated
class CQCToFile(CQCHandler): | ||
"""Handler to be used when writing the CQC commands to a file.""" | ||
|
||
def __init__(self, filename='CQC_File', pend_messages=False, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should also provide the option to specify the binary file path. Or maybe there can be an argument for if we should output text or binary and there is only one file. Also, I think the argument should instead be filepath such that the user can specify a file in another folder.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have added an argument where you choose to have binary output or text, so there's only one file.
Still to do: absolute path instead of relative.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done 8e0e607
|
||
if release_qubits and self.active_qubits: | ||
|
||
msg = self.construct_release(self.active_qubits[:], notify=True, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think some of these calls can be unified in this class and CQCConnection
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed the check for active qubits in CQCConnection to be the same as here. 9b094c5
But as for unifying, CQCConnection has a if notify:
block which this doesn't. Also in CQCConnection's close()
the classical servers also get closed
block=True, action=False) | ||
self.commit(msg) | ||
|
||
def createEPR(self, name, remote_appID=0, notify=True, block=True): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment as above, I think we don't need to have this logic in both of the subclasses. Or maybe we do but I cannot see why at this point
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean the whole createEPR
method?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, for createEPR
, recvEPR
, sendQubit
, recvQubit
and flush_factory
the code is mostly the same for the two classes. It would be better to avoid this.
This is more consistent with how the rest of Python handles specifying files. You can specify an absolute or relative path.
@AckslD You can take another look at this. For all your comments I have either solved them or left a question/remark for you to look at. |
Main changes:
New abstract class CQCHandler. This has some common functionality that is needed in both CQCConnection and CQCToFile. It also defines some abstractmethods that they must replace.
CQCConnection has been made a subclass of CQCHandler
New class CQCToFile. It is a subclass of CQCHandler. It allows writing the CQC commands to a file.