Skip to content

Commit

Permalink
Release 0.5.5: output state and set_string
Browse files Browse the repository at this point in the history
- Output complex numbers of the FINAL state of the simulator. Final
  means the last shot in case multiple shots were done.
- New JsonDict class for easy JSON output.
- Refactor simulation_result.h for simplicity and unify both simulation
  result types.
- Adapt simulator.h to use new simulation result.
- Also output JSON string in Python.
- Output is the same if c==1 or c > 1.
- Allow set_string directly
- Fix docs
  • Loading branch information
pablolh committed Jan 12, 2023
1 parent e23dff1 commit 065e5d3
Show file tree
Hide file tree
Showing 10 changed files with 299 additions and 255 deletions.
13 changes: 13 additions & 0 deletions CHANGELOG.md
Expand Up @@ -3,6 +3,19 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).

## [ 0.5.5 ] - [ 2023-01-12 ]

### Added

- set_string method to parse a cqasm string directly instead of a cqasm file

### Changed

- Fixed issue #118: quantum state is always displayed, as well as measurement register averaging

### Removed
-

## [ 0.5.4 ] - [ 2023-01-06 ]

### Added
Expand Down
2 changes: 1 addition & 1 deletion CMakeLists.txt
Expand Up @@ -120,7 +120,7 @@ set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_FULL_LIBDIR}")
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)

set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

Expand Down
29 changes: 15 additions & 14 deletions README.md
Expand Up @@ -49,20 +49,21 @@ which is perhaps the easiest way to start simulating quantum circuits.

`qxelarator` mainly provides the following API calls:

qx.set('basic.qasm') # set the required qasm to be executed on qx
qx.set_json_output_path('output.json) # set the path to the output JSON, containing
either complex amplitude (single shot) or
measurement register averaging
qx.execute() # execute the circuit once, print the final quantum
state
qx.execute(100) # execute the circuit 100 times, print measurement
register averaging
qx.get_measurement_outcome(0) # get measurement results from qubit 'n' as bool
note: in the case of repeated execution, the very
last value of the measurement register is returned
qx.get_state() # get quantum register state as string
note: in the case of repeated execution, the very
last quantum state is returned
qx.set('basic.qasm') # set the required qasm file to be executed on qx
qx.set_string('version 1.0\nqubits 2\nh q[1]') # set the required qasm string to be executed on qx
qx.set_json_output_path('output.json) # set the path to the output JSON, containing
either complex amplitude (single shot) or
measurement register averaging
json_string = qx.execute() # execute the circuit once, print the final quantum
state and measurement register, save the result as JSON in in json_string
json_string = qx.execute(100) # execute the circuit 100 times, print the very final quantum state
and measurement register averaging, save the result as JSON in in json_string
qx.get_measurement_outcome(0) # get measurement results from qubit 'n' as bool
note: in the case of repeated execution, the very
last value of the measurement register is returned
qx.get_state() # get quantum register state as string
note: in the case of repeated execution, the very
last quantum state is returned


### Installation
Expand Down
103 changes: 76 additions & 27 deletions docs/manual/usage.rst
Expand Up @@ -24,19 +24,38 @@ Then, load the cQasm file (e.g. `bell_pair.qc`):

qx.set('bell_pair.qc')

Should the parsing or analysis fail, the above call will return ``false`` and print an error.
It contains the following cQasm circuit:

If everything went well, you can execute the circuit:
::
qubits 2

.init
prep_z q[0]
prep_z q[1]

.entangle
h q[0]
cnot q[0],q[1]

.measurement
measure q[0]
measure q[1]

Alternatively, it is possible to load a string directly using ``qx.set_string(<cqasm string>)```.
Should the parsing or analysis of the file fail, the above call will return ``false`` and print an error.
If everything went well, you can execute the circuit (and obtain a json string with the output):

::

qx.execute()
>>> json_string = qx.execute()

This will print the resulting final quantum state:
This will print the resulting final quantum state as well as the resulting measurement register:

::

>>> qx.execute()
>>> json_string = qx.execute()
[QXELERATOR] Creating quantum register of 2 qubits...
[QXELERATOR] Loaded 3 circuits.
[+] executing circuit 'init' (1 iter) ...
Expand All @@ -46,32 +65,48 @@ This will print the resulting final quantum state:
[+] executing circuit 'measurement' (1 iter) ...
[+] circuit execution time: +0.202154 sec.
-------------------------------------------
Complex amplitudes with probabilities
11 1 + 0 * i (1.000000)
Final quantum state
11 1.0000 + 0.0000 * i (1.0000)

Measurement register averaging
11 1/1 (1.0000)

Since the circuit contains final measurements for all qubits, the quantum state is collapsed. If you execute the above command multiple times, you can notice
that the final quantum state changes between runs, non-deterministically.

Since the circuit contains final measurement for all qubits, the quantum state is collapsed. When removing the measurement, you obtain as expected:
When removing the measurement, you obtain a fixed quantum state:

::

Complex amplitudes with probabilities
00 0.707107 + 0 * i (0.500000)
11 0.707107 + 0 * i (0.500000)
Final quantum state
00 0.7071 + 0.0000 * i (0.5000)
11 0.7071 + 0.0000 * i (0.5000)

Add back the measurement and you can do measurement register averaging, by passing an integer number of iterations to ``execute()``:
Measurement register averaging
00 1/1 (1.0000)

The quantum state shows as expected superposition of the two qubits in the bell state.
Notice how the measurement register is then always 0, even if you repeat the execution.

Add back the measurement and you can leverage measurement register averaging, by passing an integer number of iterations to ``execute()``:

::

>>> qx.execute(100)
[QXELERATOR] Creating quantum register of 2 qubits...
[QXELERATOR] Loaded 3 circuits.
-------------------------------------------
Final quantum state
00 1.0000 + 0.0000 * i (1.0000)

Measurement register averaging
00 54/100 (0.540000)
11 46/100 (0.460000)
00 54/100 (0.4500)
11 46/100 (0.5500)

The above output means that out of 100 executions of the bell pair circuit, the measurement register had a final state of "00" in 54 cases,
and "11" in 46 cases. While this can be deducted from the final quantum state (and probabilities) obtained earlier, this way to simulate circuits becomes
way more interesting for non-deterministic circuits, that is, quantum program with mid-circuit measurement and conditional gates.
The quantum state is less relevant here, since it displays the very final quantum state after the last run of the 100 executions.
The above "Measurement register averaging" section means that out of 100 executions of the bell pair circuit, the measurement register had a final state of "00" in 54 cases,
and "11" in 46 cases. While this can be deducted from the final quantum state (and probabilities) obtained earlier on an uncollapsed state, this way to simulate circuits becomes
way more interesting for non-deterministic circuits, that is, quantum program with mid-circuit measurements and conditional gates.

For example, simulate the following circuit:

Expand All @@ -94,6 +129,9 @@ and get:
[QXELERATOR] Creating quantum register of 3 qubits...
[QXELERATOR] Loaded 1 circuits.
-------------------------------------------
Final quantum state
001 1.0000 + 0.0000 * i (1.0000)

Measurement register averaging
000 504/1000 (0.504000)
001 257/1000 (0.257000)
Expand All @@ -119,18 +157,29 @@ After another ``execute(1000)`` call, that JSON output will look like this:

> cat simulation_result.json
{
"info": {
"shots_requested": 1000,
"shots_done": 1000
},
"results": {
"000": 0.516000,
"001": 0.241000,
"011": 0.243000
}
"info": {
"shots_requested": 1000,
"shots_done": 1000
},
"results": {
"000": 0.516000,
"001": 0.241000,
"011": 0.243000
},
"state": {
"001": {
"real": 1.000000,
"imag": 0.000000
}
}
}

Note: ``shots_done`` will always equal to ``shots_requested``. These two keys are distinct so that hardware backends can output the same JSON keys and optionally
Note 1: The json string ``json_string``` obtained as output of ``json_string = qx.execute(n)``` is equal to the content of this file.

Note 2 (!IMPORTANT!): When doing a single run (e.g. ``qx.execute()``` or ``qx.execute(1)```), the "results" section contains the norms of the complex amplitudes (that is, probabilities of measuring each quantum state).
When doing n>=2 runs, the same section contains averages of the measurement register.

Note 3: ``shots_done`` will always equal to ``shots_requested``. These two keys are distinct so that hardware backends can output the same JSON keys and optionally
notify that they have failed to execute all requested runs.

Running the binary built from source
Expand Down

0 comments on commit 065e5d3

Please sign in to comment.