# Quantum Programming Studio API
Python wrapper for [Quantum Programming Studio](https://quantum-circuit.com/) HTTP API.
## Install and configure
**1. Install QPS API package:**
```bash
pip install quantastica-qps-api
```
**2. Find your QPS API token:**
[Login](https://quantum-circuit.com/login) to Quantum Programming Studio, go to [Profile -> API Access](https://quantum-circuit.com/user_settings/api) and copy your API token.
**3. Configure QPS API package with your API token:**
```python
from quantastica.qps_api import QPS
QPS.save_account("YOUR_API_TOKEN")
```
That will create a local configuration file where your API token will be stored for future use.
**Now you are ready to use QPS API.**
## Account management
**QPS.save_account(api_token, api_url=None)**
**Run this once** to setup your QPS REST API account. Method will create configuration file and your api token will be stored there for future use.
If needed, you can clear your token by running `QPS.save_account("")` (or by deleting a configuration file).
If `api_url` is not provided then `https://quantum-circuit.com/api/` will be set as default.
**QPS.config_path()**
You can get config file path by running `QPS.config_path()`.
Default configuration file path:
- On Unix, directory is obtained from environment variable HOME if it is set; otherwise the current user’s home directory is looked up in the password directory through the built-in module pwd.
- On Windows, USERPROFILE will be used if set, otherwise a combination of HOMEPATH and HOMEDRIVE will be used.
## Synthesis and transpilation API
Synthesis and transpilation tool can be used to:
- create quantum circuit from state vectors
- create quantum circuit from truth table
- create quantum circuit from unitary matrix (decompose unitary matrix)
- transpile circuits (change instruction set)
### Circuit from vectors
Find quantum circuit from pairs of initial & final state vectors (wave functions).
**QPS.synth.circuit_from_vectors(vector_pairs, endianness = "little", job_name=None, settings = {}, start_job=True)**
- `vector_pairs` is list containing vector pairs. Each vector pair is list with 2 elements: initial vector and final vector. All vectors in all pairs must be of same length (same number of qubits).
- `endianness` string. Orientation of bits in state vector (most significant bit/first qubit or least significant bit/first qubit). Can be `little` (like Qiskit) or `big`. Default is `little`.
- `job_name` string is optional. You can give it a human readable name.
- `start_job` if this argument is `True` (default) the job will be immediatelly sent to execution queue. If `start_job` is `False` then it will stay in `draft` state and you will be able to start it later by calling `start_job()` method.
- `settings` object is optional. Default is:
```python
{
"strategy": "strategy_a",
"pre_processing": "",
"allowed_gates": "u3,cx",
"min_gates": 0,
"max_gates": 0,
"max_diff": 0.001,
"diff_method": "ignorephase",
"max_duration": 0,
"single_solution": True
}
```
**Settings**
- `strategy` string. Can be one of: `strategy_a` (brute force or heuristics) and `strategy_b` OptigenQ+QSD (OptigenQ is Quantastica's method to find unitary matrix from pairs of vectors, and QSD is used to decompose matrix and return circuit). Default is `strategy_a`.
- `pre_processing` string. Used only with `strategy_a` (brute force or heuristics). Can be one of: `stable`, `experimental_1`, `experimental_2`, `experimental_3`, `experimental_5`. Default is empty string (Empty string means "use default" which is currently `stable`).
- `allowed_gates` string. Used only with `strategy_a` (brute force or heuristics). Comma delimited gate names (instruction set) to use. Default is `u3,cx`. With `strategy_b` instruction set is fixed to `u3,cx` (you can transpile returned circuit later using `QPS.synth.transpile()` method.
- `min_gates` and `max_gates` integer. Used only with `strategy_a` (brute force or heuristics). Default is `0` (no limits).
- `max_diff` float. Used only with `strategy_a` (brute force or heuristics). Default is `0.001` (1e-3).
- `diff_method` string. Used only with `strategy_a` (brute force or heuristics). Can be one of: `distance` (exact match), `ignorephase` (match up to a global phase) and `abs` (match absolute values). Default is `ignorephase`.
- `max_duration` integer. Timeout in seconds. Solver will stop after specified number of seconds and error will be returned. Useful with brute force algorithm which has high computational complexity and can run for a very long time. You can decide when to stop (and for example proceed with the next job which is using different method and is waiting in a queue - if you prepared it). Default is `0` which means "maximum allowed by your subscription plan". Free plan has limit of a few seconds (subject to change).
- `single_solution` boolean. Used only with `strategy_a` (brute force or heuristics). When `True`, solver will stop when first solution was found. When `False` solver will return all possible configurations of a circuit.
Note: if `settings` argument is provided, it will overwrite default settings, but only provided keys will be overwritten - not entire default settings object.
**Example:**
```python
from quantastica.qps_api import QPS
vector_pairs = [
[ [1, 0, 0, 0], [ 0.5+0j, 0.5+0j, 0.5+0j, 0.5+0j ] ],
[ [0, 1, 0, 0], [ 0.5+0j, 0+0.5j, -0.5+0j, 0-0.5j ] ],
[ [0, 0, 1, 0], [ 0.5+0j, -0.5+0j, 0.5+0j, -0.5+0j ] ],
[ [0, 0, 0, 1], [ 0.5+0j, 0-0.5j, -0.5+0j, 0+0.5j ] ]
]
job_id = QPS.synth.circuit_from_vectors(vector_pairs, settings = { "instruction_set": ["h", "cu1", "swap"], "single_solution": False })
job = QPS.synth.get_job(job_id, wait=True)
job_status = job["status"]
job_output = job["output"]
if(job_status == "error"):
print(job_output["message"])
else:
for circuit in job_output["circuits"]:
print(circuit["qasm"])
```
Example output:
```
OPENQASM 2.0;
include "qelib1.inc";
qreg q[2];
h q[1];
swap q[0], q[1];
cu1 (2.356194496154785) q[0], q[1];
h q[1];
OPENQASM 2.0;
include "qelib1.inc";
qreg q[2];
h q[1];
cu1 (2.356194496154785) q[0], q[1];
h q[0];
swap q[0], q[1];
OPENQASM 2.0;
include "qelib1.inc";
qreg q[2];
h q[1];
cu1 (2.356194496154785) q[0], q[1];
swap q[0], q[1];
h q[1];
OPENQASM 2.0;
include "qelib1.inc";
qreg q[2];
swap q[0], q[1];
h q[0];
cu1 (2.356194496154785) q[0], q[1];
h q[1];
```
### State preparation
Get circuit which prepares provided state.
**QPS.synth.state_preparation(final_vector, endianness = "little", job_name=None, settings = {}, start_job=True)**
- `final_vector` is target vector.
- `endianness` string. Orientation of bits in state vector (most significant bit/first qubit or least significant bit/first qubit). Can be `little` (like Qiskit) or `big`. Default is `little`.
- `job_name` string is optional. You can give it a human readable name.
- `start_job` if this argument is `True` (default) the job will be immediatelly sent to execution queue. If `start_job` is `False` then it will stay in `draft` state and you will be able to start it later by calling `start_job()` method.
- `settings` object is optional. Default is:
```python
{
"strategy": "strategy_a",
"pre_processing": "",
"allowed_gates": "u3,cx",
"min_gates": 0,
"max_gates": 0,
"max_diff": 0.001,
"diff_method": "ignorephase",
"max_duration": 0,
"single_solution": True
}
```
**Settings**
- `strategy` string. Can be one of: `strategy_a` (brute force or heuristics) and `strategy_b` OptigenQ+QSD (OptigenQ is Quantastica's method to find unitary matrix from pairs of vectors, after which QSD is used to decompose matrix and return circuit). Default is `strategy_a`.
- `pre_processing` string. Used only with `strategy_a` (brute force or heuristics). Can be one of: `stable`, `experimental_1`, `experimental_2`, `experimental_3`, `experimental_5`. Default is empty string (Empty string means "use default" which is currently `stable`).
- `allowed_gates` string. Used only with `strategy_a` (brute force or heuristics). Comma delimited gate names (instruction set) to use. Default is `u3,cx`. With `strategy_b` instruction set is fixed to `u3,cx` (you can transpile returned circuit later using `QPS.synth.transpile()` method.
- `min_gates` and `max_gates` integer. Used only with `strategy_a` (brute force or heuristics). Default is `0` (no limits).
- `max_diff` float. Used only with `strategy_a` (brute force or heuristics). Default is `0.001` (1e-3).
- `diff_method` string. Used only with `strategy_a` (brute force or heuristics). Can be one of: `distance` (exact match), `ignorephase` (match up to a global phase) and `abs` (match absolute values). Default is `ignorephase`.
- `max_duration` integer. Timeout in seconds. Solver will stop after specified number of seconds and error will be returned. Useful with brute force algorithm which has high computational complexity and can run for a very long time. You can decide when to stop (and for example proceed with the next job which is using different method and is waiting in a queue - if you prepared it). Default is `0` which means "maximum allowed by your subscription plan". Free plan has limit of a few seconds (subject to change).
- `single_solution` boolean. Used only with `strategy_a` (brute force or heuristics). When `True`, solver will stop when first solution was found. When `False` solver will return all possible configurations of a circuit.
Note: if `settings` argument is provided, it will overwrite default settings, but only provided keys will be overwritten - not entire default settings object.
**Example:**
```python
from quantastica.qps_api import QPS
desired_state = [0.5, 0.5, 0.5, 0.5]
job_id = QPS.synth.state_preparation(desired_state, settings = { "instruction_set": ["u3", "cx"] })
job = QPS.synth.get_job(job_id, wait=True)
job_status = job["status"]
job_output = job["output"]
if(job_status == "error"):
print(job_output["message"])
else:
for circuit in job_output["circuits"]:
print(circuit["qasm"])
```
Example output:
```
OPENQASM 2.0;
include "qelib1.inc";
qreg q[2];
u3 (1.570796370506287, 0, 1.217840194702148) q[0];
u3 (1.570796370506287, 0, 0.621559917926788) q[1];
```
### Transpile
Transpile circuit (change instruction set).
**QPS.synth.transpile(input_qasm, method="replace_blocks", method_options={}, job_name=None, settings = {}, start_job=True)**
- `input_qasm` is string containing OpenQASM 2.0 code.
- `method` is method name string. Can be one of: "replace_circuit", "replace_blocks", "replace_gates". Default: "replace_blocks".
- `method_options` dict with following structure:
- If method is `replace_blocks` then: `{ "block_size": 2, "two_pass": False }` (maximum block size is 4).
- For other methods: no options (`method_options` is ignored)
- `job_name` string is optional. You can give it a human readable name.
- `start_job` if this argument is `True` (default) the job will be immediatelly sent to execution queue. If `start_job` is `False` then it will stay in `draft` state and you will be able to start it later by calling `start_job()` method.
- `settings` object is optional. Default is:
```python
{
"pre_processing": "experimental1",
"allowed_gates": "u3,cx",
"max_diff": 0.001,
"diff_method": "ignorephase",
"max_duration": 0
}
```
**Settings**
- `pre_processing` string. Can be one of: `stable`, `experimental_1`, `experimental_2`, `experimental_3`, `experimental_5`. Default is empty string (Empty string means "use default" which is currently `stable`).
- `allowed_gates` string. Comma delimited gate names (instruction set) to use. Default is `u3,cx`.
- `max_diff` float. Default is `0.001` (1e-3).
- `diff_method` string. Can be one of: `distance` (exact match), `ignorephase` (match up to a global phase) and `abs` (match absolute values). Default is `ignorephase`.
- `max_duration` integer. Timeout in seconds. Solver will stop after specified number of seconds and error will be returned. Default is `0` which means "maximum allowed by your subscription plan". Free plan has limit of a few seconds (subject to change).
Note: if `settings` argument is provided, it will overwrite default settings, but only provided keys will be overwritten - not entire default settings object.
**Example:**
```python
from quantastica.qps_api import QPS
input_qasm = """
OPENQASM 2.0;
include "qelib1.inc";
qreg q[2];
h q[0];
cx q[0], q[1];
"""
job_id = QPS.synth.transpile(input_qasm, settings = { "instruction_set": ["id", "x", "sx", "rz", "cx"], "diff_method": "ignorephase" })
job = QPS.synth.get_job(job_id, wait=True)
job_status = job["status"]
job_output = job["output"]
if(job_status == "error"):
print(job_output["message"])
else:
for circuit in job_output["circuits"]:
print(circuit["qasm"])
```
Example output:
```
OPENQASM 2.0;
include "qelib1.inc";
qreg q[2];
sx q[0];
rz (1.570796370506287) q[0];
sx q[0];
cx q[0], q[1];
```
### Decompose matrix
Decompose unitary matrix (find circuit from matrix).
**QPS.synth.decompose_unitary(unitary, endianness="big", job_name=None, settings = {}, start_job=True)**
- `unitary` matrix operator.
- `endianness` - orientation of the matrix. Can be `little` endian (like Qiskit) or `big` endian. Default is `big`. Note that default endianness of the matrix differs from default endianness of vectors in other methods. That's to be aligned with QPS. In Qiskit, both matrices and vectors are `little` endian. So, if you are solving unitary from Qiskit then provide `endianness = "little"` argument.
- `job_name` string is optional. You can give it a human readable name.
- `start_job` if this argument is `True` (default) the job will be immediatelly sent to execution queue. If `start_job` is `False` then it will stay in `draft` state and you will be able to start it later by calling `start_job()` method.
- `settings` object is optional. Default is:
```python
{
"strategy": "strategy_a",
"pre_processing": "",
"allowed_gates": "u3,cx",
"coupling_map": [],
"min_gates": 0,
"max_gates": 0,
"max_diff": 0.001,
"diff_method": "ignorephase",
"max_duration": 0,
"single_solution": True
}
```
**Settings**
- `strategy` string. Can be one of: `strategy_a` (brute force or heuristics) and `strategy_b` QSD. Default is `strategy_a`. Note that `strategy_a` (brute force or heuristics) returns circuit with optimal or near optimal depth, but this method has very high computational complexity and can run for a very long time (depending on number of qubits and total number of gates in the resulting circuit).
- `pre_processing` string. Used only with `strategy_a` (brute force or heuristics). Can be one of: `stable`, `experimental_1` and `experimental_5`. Default is empty string (Empty string means "use default" which is currently `stable`). Experimental methods have lower computational complexity and should finish sooner, but that depends on many factors, so you should try all methods and see what works best for your particular problem.
- `allowed_gates` string. Used only with `strategy_a` (brute force or heuristics). Comma delimited gate names (instruction set) to use. Default is `u3,cx`. With `strategy_b` instruction set is fixed to `u3,cx` (you can transpile returned circuit later using `QPS.synth.transpile()` method.
- `min_gates` and `max_gates` integer. Used only with `strategy_a` (brute force or heuristics). Default is `0` (no limits).
- `max_diff` float. Used only with `strategy_a` (brute force or heuristics). Default is `0.001` (1e-3).
- `diff_method` string. Used only with `strategy_a` (brute force or heuristics). Can be one of: `distance` (exact match), `ignorephase` (match up to a global phase) `hs` (match up to a global phase using faster method) and `abs` (match absolute values). Default is `ignorephase`.
- `max_duration` integer. Timeout in seconds. Solver will stop after specified number of seconds and error will be returned. Useful with brute force algorithm which has high computational complexity and can run for a very long time. You can decide when to stop (and for example proceed with the next job which is using different method and is waiting in a queue - if you prepared it). Default is `0` which means "maximum allowed by your subscription plan". Free plan has limit of a few seconds (subject to change).
- `single_solution` boolean. Used only with `strategy_a` (brute force or heuristics). When `True`, solver will stop when first solution was found. When `False` solver will return all possible configurations of a circuit.
Note: if `settings` argument is provided, it will overwrite default settings, but only provided keys will be overwritten - not entire default settings object.
**Example:**
```python
from quantastica.qps_api import QPS
unitary = [
[ 0.5+0.0j, 0.5+0.0j, 0.5+0.0j, 0.5+0.0j],
[ 0.5+0.0j, 0.5+0.0j, -0.5+0.0j, -0.5+0.0j],
[ 0.5+0.0j, -0.5+0.0j, 0.0+0.5j, 0.0-0.5j],
[ 0.5+0.0j, -0.5+0.0j, 0.0-0.5j, 0.0+0.5j]
]
job_id = QPS.synth.decompose_unitary(unitary, settings = { "instruction_set": ["h", "cu1", "swap"], "single_solution": False })
job = QPS.synth.get_job(job_id, wait=True)
job_status = job["status"]
job_output = job["output"]
if(job_status == "error"):
print(job_output["message"])
else:
for circuit in job_output["circuits"]:
print(circuit["qasm"])
```
Example output:
```
OPENQASM 2.0;
include "qelib1.inc";
qreg q[2];
h q[1];
swap q[0], q[1];
cu1 (1.570796370506287) q[0], q[1];
h q[1];
OPENQASM 2.0;
include "qelib1.inc";
qreg q[2];
h q[1];
cu1 (1.570796370506287) q[0], q[1];
h q[0];
swap q[0], q[1];
OPENQASM 2.0;
include "qelib1.inc";
qreg q[2];
h q[1];
cu1 (1.570796370506287) q[0], q[1];
swap q[0], q[1];
h q[1];
OPENQASM 2.0;
include "qelib1.inc";
qreg q[2];
swap q[0], q[1];
h q[0];
cu1 (1.570796370506287) q[0], q[1];
h q[1];
```
### Create algorithm from truth table
Create circuit which implements logical expression whose truth table is given.
**QPS.synth.circuit_from_truth_table(truth_table_csv, column_defs, csv_delimiter=None, additional_qubits=1, job_name=None, settings={}, start_job=True)**
- `truth_table_csv` is string containing truth table in CSV format
- `column_defs` list of strings describing each column from truth table: `"input"`, `"output"` or `"ignore"`
- `csv_delimiter` CSV column delimiter char: `None`, `","` (comma) or `"\t"` (tab). If delimiter is `None` (default) it will be automatically detected.
- `additional_qubits` number of qubits to add (to displace input and output qubits).
- `job_name` string is optional. You can give it a human readable name.
- `settings` object is optional. Default is:
```python
{
"pre_processing": "",
"allowed_gates": "x,cx,ccx,swap",
"coupling_map": [],
"min_gates": 0,
"max_gates": 0,
"max_diff": 0.001,
"diff_method": "ignorephase",
"max_duration": 0,
"single_solution": True
}
```
**Settings**
- `pre_processing` string. Can be one of: `stable`, `experimental_1` and `experimental_5`. Default is empty string (Empty string means "use default" which is currently `stable`).
- `allowed_gates` string. Comma delimited gate names (instruction set) to use. Default is `x,cx,ccx,swap`.
- `min_gates` and `max_gates` integer. Default is `0` (no limits).
- `max_diff` float. Used only with `strategy_a` (brute force or heuristics). Default is `0.001` (1e-3).
- `diff_method` string. Used only with `strategy_a` (brute force or heuristics). Can be one of: `distance` (exact match), `ignorephase` (match up to a global phase) and `abs` (match absolute values). Default is `ignorephase`.
- `max_duration` integer. Timeout in seconds. Solver will stop after specified number of seconds and error will be returned. Useful with brute force algorithm which has high computational complexity and can run for a very long time. You can decide when to stop (and for example proceed with the next job which is using different method and is waiting in a queue - if you prepared it). Default is `0` which means "maximum allowed by your subscription plan". Free plan has limit of a few seconds (subject to change).
- `single_solution` boolean. Used only with `strategy_a` (brute force or heuristics). When `True`, solver will stop when first solution was found. When `False` solver will return all possible configurations of a circuit.
Note: if `settings` argument is provided, it will overwrite default settings, but only provided keys will be overwritten - not entire default settings object.
**Example:**
```python
from quantastica.qps_api import QPS
truth_table = """
A,B,A_NAND_B
0,0,1
0,1,1
1,0,1
1,1,0
"""
job_id = QPS.synth.circuit_from_truth_table(truth_table, ["input", "input", "output"])
job = QPS.synth.get_job(job_id, wait=True)
job_status = job["status"]
job_output = job["output"]
if(job_status == "error"):
raise Exception(job_output["message"])
else:
if(len(job_output["circuits"]) == 0):
raise Exception("No results.")
else:
for circuit in job_output["circuits"]:
print(circuit["qasm"])
```
Example output:
```
OPENQASM 2.0;
include "qelib1.inc";
qreg q[3];
x q[2];
ccx q[0], q[1], q[2];
```
### Run problem file
Solve problem provided in internal format used by synthesizer and transpiler.
**QPS.synth.solve(problem, settings = {}, start_job=True)**
- `problem` object (e.g. job exported to json from Quantum Programming studio).
- `settings` argument is optional. If provided, it will overwrite keys in `problem.settings`. Note that only provided keys will be overwritten - not entire `problem.settings` object.
- `start_job` if this argument is `True` (default) the job will be immediatelly sent to execution queue. If `start_job` is `False` then it will stay in `draft` state and you will be able to start it later by calling `start_job()` method.
**Example:**
```python
from quantastica.qps_api import QPS
problem = {
"name": "Bell",
"type": "vectors",
"source": {
"vectors": {
"text1": "[ 1, 0, 0, 0 ]",
"text2": "[ 1/sqrt(2), 0, 0, 1/sqrt(2) ]",
"endianness1": "little",
"endianness2": "little"
}
},
"problem": [
{
"input": [
1,
0,
0,
0
],
"output": [
0.7071067811865475,
0,
0,
0.7071067811865475
]
}
],
"settings": {
"strategy": "strategy_a",
"pre_processing": "",
"allowed_gates": "u3,cx",
"coupling_map": [],
"min_gates": 0,
"max_gates": 0,
"max_diff": 0.001,
"diff_method": "distance",
"max_duration": 0,
"single_solution": False
}
}
job_id = QPS.synth.solve(problem)
job = QPS.synth.get_job(job_id, wait=True)
job_status = job["status"]
job_output = job["output"]
if(job_status == "error"):
print(job_output["message"])
else:
for circuit in job_output["circuits"]:
print(circuit["qasm"])
```
Example output:
```
OPENQASM 2.0;
include "qelib1.inc";
qreg q[2];
u3 (-1.570796370506287, -3.141592741012573, -2.675650835037231) q[0];
cx q[0], q[1];
OPENQASM 2.0;
include "qelib1.inc";
qreg q[2];
u3 (-1.570796370506287, -3.141592741012573, -2.675650835037231) q[1];
cx q[1], q[0];
```
### Synthesizer/transpiler output format
Finished job object has following structure:
```javascript
{
"name" : String,
"type" : String,
"source" : Object,
"problem" : Array,
"settings" : Object,
"status" : String,
"output" : Object,
"queuedAt" : String,
"startedAt" : String,
"finishedAt" : String
}
```
Keys important to user are:
- `name` String: name of the job
- `status` String: can be `draft`, `queued`, `running`, `error`, `done`.
- `output` Object with following structure:
```javascript
{
"error_code" : Integer,
"message" : String,
"time_taken" : Float,
"version" : String,
"circuits" : Array of Object
}
```
- `error_code` Integer: 0 on success, non-zero on error
- `message` String: error message if error code is non-zero
- `time_taken` Float: number of seconds
- `version` String: solver version
- `circuits` Array: resulting circuits. Each is object with following structure:
```javascript
{
"qubits" : Integer,
"cregs" : Array,
"program" : Array,
"diff" : Float,
"index" : Integer,
"qasm" : String,
"qasmExt" : String
}
```
Keys important to user are:
- `qasm` OpenQASM 2.0 source code of the resulting circuit.
- `qasmExt` OpenQASM 2.0 with extended instruction set (all gates supported by Quantum Programming Studio).
Difference between `qasm` and `qasmExt`: if circuit contains gate supported by QPS but not directly supported by OpenQASM 2.0 then `qasm` will contain equivalent circuit transpiled to OpenQASM 2.0 instruction set, but `qasmExt` will contain gates as is.
For example if circuit contains IONQ native gate `gpi2(2.51678906856393)` on first qubit:
`qasm` will be:
```
OPENQASM 2.0;
include "qelib1.inc";
qreg q[1];
u3 (1.5707963267948966, 0.9459927417690333, -0.9459927417690333) q[0];
```
`qasmExt` will contain:
```
OPENQASM 2.0;
include "qelib1.inc";
qreg q[1];
gpi2 (2.51678906856393) q[0];
```
**Example job object with output:**
```python
{
"_id": "r9LskFoLPQW5w7HTp",
"name": "Bell",
"type": "vectors",
"source": {
"vectors": {
"text1": "[ 1, 0, 0, 0 ]",
"text2": "[ 1/sqrt(2), 0, 0, 1/sqrt(2) ]",
"endianness1": "little",
"endianness2": "little"
}
},
"problem": [
{
"input": [
1,
0,
0,
0
],
"output": [
0.7071067811865475,
0,
0,
0.7071067811865475
]
}
],
"settings": {
"strategy": "strategy_a",
"pre_processing": "",
"allowed_gates": "u3,cx",
"coupling_map": [],
"min_gates": 0,
"max_gates": 0,
"max_diff": 0.001,
"diff_method": "distance",
"max_duration": 0,
"single_solution": False
},
"output": {
"circuits": [
{
"qubits": 2,
"cregs": [],
"diff": 0,
"program": [
{
"name": "u3",
"wires": [
0
],
"options": {
"params": {
"theta": 1.570796326794896,
"phi": 0,
"lambda": 1.456034103897321
}
}
},
{
"name": "cx",
"wires": [
0,
1
],
"options": {}
}
],
"index": 0
},
{
"qubits": 2,
"cregs": [],
"diff": 0,
"program": [
{
"name": "u3",
"wires": [
1
],
"options": {
"params": {
"theta": 1.570796326794896,
"phi": 0,
"lambda": 1.456034103897321
}
}
},
{
"name": "cx",
"wires": [
1,
0
],
"options": {}
}
],
"index": 1
}
],
"error_code": 0,
"message": "",
"time_taken": 0.008,
"version": "0.1.0",
"stats": {
"result_count": 2
}
},
"createdAt": "2021-02-06T23:39:29.108Z",
"modifiedAt": "2021-02-06T23:39:30.383Z",
"queuedAt": "2021-02-06T23:39:29.676Z",
"startedAt": "2021-02-06T23:39:29.926Z",
"finishedAt": "2021-02-06T23:39:30.383Z",
"status": "done"
}
```
### Using synthesizer and transpiler with Qiskit
Format used for input and output is OpenQASM 2.0, so integration with Qiskit (and other frameworks that support OpenQASM) is easy.
**Example** transpile Qiskit circuit:
```python
from qiskit import QuantumCircuit
from qiskit.circuit.random import random_circuit
from qiskit.quantum_info import Operator
from quantastica.qps_api import QPS
# Generate random Qiskit circuit
qc = random_circuit(5, 5, measure=False)
# Get QASM code
input_qasm = qc.qasm()
# Transpile with QPS
job_id = QPS.synth.transpile(input_qasm, settings = { "instruction_set": ["id", "u3", "cx"], "diff_method": "ignorephase" })
job = QPS.synth.get_job(job_id, wait=True)
job_status = job["status"]
job_output = job["output"]
if(job_status == "error"):
raise Exception(job_output["message"])
transpiled_circuit = job_output["circuits"][0]
# Get QASM code
transpiled_qasm = transpiled_circuit["qasm"]
# Create Qiskit circuit
transpiled_qc = QuantumCircuit.from_qasm_str(transpiled_qasm)
# Show circuit
print("Depth:", transpiled_qc.depth())
print("Ops:", sum(j for i, j in transpiled_qc.count_ops().items()))
display(transpiled_qc.draw(output="mpl"))
```
### Job management
Problem sent to solver (synthesizer or transpiler) is called a "job". Each job has unique ID. Solver is resource intensive tool, so it is configured to execute only one job at a time. While solver is processing a job, other jobs are queued. When solver finishes a job, it takes the next one from the queue.
API provides functions for job manipulation: you can list all jobs (filtered by status), stop running job, cancel queued jobs, stop/cancel all jobs, start previously canceled (draft) job, etc.
**QPS.synth.list_jobs(status_filter=None)**
List all jobs, optionally filtered by status.
- `status_filter` String, optional. Can be: `draft`, `queued`, `running`, `error`, `done`.
**Example 1** - list all (unfiltered) jobs:
```python
from quantastica.qps_api import QPS
jobs = QPS.synth.list_jobs()
print(jobs)
```
Example output:
```python
{
"list": [
{ "_id": "r9LskFoLPQW5w7HTp", "name": "Bell state", "type": "vectors", "status": "done" },
{ "_id": "R8tJH7XoZ233oTREy", "name": "4Q Gauss", "type": "vectors", "status": "queued" },
{ "_id": "h7fzYbFz8MJvkNhiX", "name": "Challenge", "type": "unitary", "status": "draft" },
{ "_id": "PC5PNXiGqhh2HmkX8", "name": "Experiment", "type": "vectors", "status": "error"},
{ "_id": "SNhiCqSCT2WwRWKCd", "name": "Decompose", "type": "unitary", "status": "running" }
]
}
```
**Example 2** - list `running` jobs:
```python
from quantastica.qps_api import QPS
jobs = QPS.synth.list_jobs(status_filter="running")
print(jobs)
```
Example output:
```python
{
"list": [
{ "_id": "SNhiCqSCT2WwRWKCd", "name": "Decompose", "type": "unitary", "status": "running" }
]
}
```
**QPS.synth.job_status(job_id)**
Get job status.
**Example:**
```python
from quantastica.qps_api import QPS
status = QPS.synth.job_status("PC5PNXiGqhh2HmkX8")
print(status)
```
Example output:
```python
{ "_id": "PC5PNXiGqhh2HmkX8", "name": "Experiment", "type": "vectors", "status": "error", "message": "connect ECONNREFUSED" }
```
**QPS.synth.get_job(job_id, wait=True)**
Get job referenced by ID. If `wait` argument is `True` (default), then function will wait for a job to finish (or fail) before returning. If `wait` is `False`, then job will be immediatelly returned even if it is still running (in which case it will not contain a solution).
**Example:**
```python
from quantastica.qps_api import QPS
job = QPS.synth.get_job("r9LskFoLPQW5w7HTp")
print(job)
```
Example output:
```python
{
"_id": "r9LskFoLPQW5w7HTp",
"name": "Bell",
"type": "vectors",
"source": {
"vectors": {
"text1": "[ 1, 0, 0, 0 ]",
"text2": "[ 1/sqrt(2), 0, 0, 1/sqrt(2) ]",
"endianness1": "little",
"endianness2": "little"
}
},
"problem": [
{
"input": [ 1, 0, 0, 0 ],
"output": [ 0.7071067811865475, 0, 0, 0.7071067811865475 ]
}
],
"settings": {
"max_duration": 0,
"allowed_gates": "u3,cx",
"coupling_map": [],
"min_gates": 0,
"max_gates": 0,
"pre_processing": "",
"strategy": "strategy_a",
"max_diff": 0.001,
"diff_method": "distance",
"single_solution": False
},
"status": "done",
"output": {
"circuits": [
{
"qubits": 2,
"cregs": [],
"diff": 0,
"program": [
{
"name": "u3",
"wires": [ 0 ],
"options": {
"params": {
"theta": -1.570796370506287,
"phi": -3.141592741012573,
"lambda": -5.327113628387451
}
}
},
{
"name": "cx",
"wires": [ 0, 1 ],
"options": {}
}
],
"index": 0,
"qasm": "OPENQASM 2.0;\ninclude \"qelib1.inc\";\nqreg q[2];\nu3 (-1.570796370506287, -3.141592741012573, -5.327113628387451) q[0];\ncx q[0], q[1];\n",
"qasmExt": "OPENQASM 2.0;\ninclude \"qelib1.inc\";\nqreg q[2];\nu3 (-1.570796370506287, -3.141592741012573, -5.327113628387451) q[0];\ncx q[0], q[1];\n"
},
{
"qubits": 2,
"cregs": [],
"diff": 0,
"program": [
{
"name": "u3",
"wires": [ 1 ],
"options": {
"params": {
"theta": -1.570796370506287,
"phi": -3.141592741012573,
"lambda": -5.327113628387451
}
}
},
{
"name": "cx",
"wires": [ 1, 0 ],
"options": {}
}
],
"index": 1,
"qasm": "OPENQASM 2.0;\ninclude \"qelib1.inc\";\nqreg q[2];\nu3 (-1.570796370506287, -3.141592741012573, -5.327113628387451) q[1];\ncx q[1], q[0];\n",
"qasmExt": "OPENQASM 2.0;\ninclude \"qelib1.inc\";\nqreg q[2];\nu3 (-1.570796370506287, -3.141592741012573, -5.327113628387451) q[1];\ncx q[1], q[0];\n"
}
],
"error_code": 0,
"message": "",
"time_taken": 0.002,
"version": "0.1.0"
},
"queuedAt": "2021-02-06T23:39:29.676Z",
"startedAt": "2021-02-06T23:39:29.926Z",
"finishedAt": "2021-02-06T23:39:30.383Z"
}
```
**QPS.synth.stop_job(job_id)**
Stop running or cancel queued job. Job will be put into `draft` state, and you can start it again later by calling `start_job()`.
**Example:**
```python
from quantastica.qps_api import QPS
response = QPS.synth.stop_job("SNhiCqSCT2WwRWKCd")
print(response)
```
Example output:
```python
{ "_id": "SNhiCqSCT2WwRWKCd", message: "OK" }
```
**QPS.synth.stop_all_jobs(status_filter=None)**
Stop running job / cancel all queued jobs.
- `status_filter` - you can stop only a running job by providing `status_filter="running"` (after this, next job from the queue will be executed). Or, you can cancel all queued jobs by providing `status_filter="queued"` (running job will not be affected - it will continue running).
**Example 1** - stop running job and remove all jobs from queue:
```python
from quantastica.qps_api import QPS
stopped = QPS.synth.stop_all_jobs()
print(stopped)
```
Example output:
```python
{
"stopped": [
{ "_id": "SNhiCqSCT2WwRWKCd", "name": "Decompose", "type": "unitary" },
{ "_id": "R8tJH7XoZ233oTREy", "name": "4Q Gauss", "type": "vectors" }
]
}
```
**Example 2** - stop only a running job. Next job from queue, if any, will start:
```python
from quantastica.qps_api import QPS
stopped = QPS.synth.stop_all_jobs(status_filter="running")
print(stopped)
```
Example output:
```python
{
"stopped": [
{ "_id": "SNhiCqSCT2WwRWKCd", "name": "Decompose", "type": "unitary" }
]
}
```
**Example 3** - cancel all queued jobs. Running job will not be affected:
```python
from quantastica.qps_api import QPS
stopped = QPS.synth.stop_all_jobs(status_filter="queued")
print(stopped)
```
Example output:
```python
{
"stopped": [
{ "_id": "R8tJH7XoZ233oTREy", "name": "4Q Gauss", "type": "vectors" }
]
}
```
**QPS.synth.start_job(job_id)**
Start previously stopped/canceled job (can be any job with status `draft`).
**Example:**
```python
from quantastica.qps_api import QPS
response = QPS.synth.start_job("SNhiCqSCT2WwRWKCd")
print(response)
```
Example output:
```python
{ "_id": "SNhiCqSCT2WwRWKCd", "message": "OK" }
```
## Quantum Language Converter API
[Quantum Language Converter](https://quantastica.com/#converters) is a tool which converts quantum program between different quantum programming languages and frameworks. It is also available as a [q-convert](https://www.npmjs.com/package/q-convert) command line tool and as a web UI at [https://quantum-circuit.com/qconvert](https://quantum-circuit.com/qconvert).
QPS has integrated quantum language converter API which you can access directly from python code:
**QPS.converter.convert(input, source, dest)**
Converts `input` quantum program given as string from `source` format into `dest` format.
- `input` String. Program source code
- `source` String. Input format:
- `qasm` [OpenQASM 2.0](https://github.com/Qiskit/openqasm) source code
- `quil` [Quil](https://arxiv.org/abs/1608.03355) source code
- `qobj` [QObj](https://arxiv.org/abs/1809.03452)
- `ionq` [IONQ](https://docs.ionq.com/) (json)
- `quantum-circuit` [quantum-circuit](https://www.npmjs.com/package/quantum-circuit) object (json)
- `toaster` [Qubit Toaster](https://quantastica.com/#toaster) object (json)
- `dest` String. Output format:
- `qiskit` [Qiskit](https://qiskit.org/documentation/)
- `qasm` [OpenQASM 2.0](https://github.com/Qiskit/openqasm)
- `qasm-ext` OpenQASM 2.0 with complete instruction set supported by QPS (and other Quantastica tools)
- `qobj` [QObj](https://arxiv.org/abs/1809.03452)
- `quil` [Quil](https://arxiv.org/abs/1608.03355)
- `pyquil` [pyQuil](http://docs.rigetti.com/en/latest/index.html)
- `braket` [Braket](https://docs.aws.amazon.com/braket/)
- `cirq` [Cirq](https://github.com/quantumlib/Cirq)
- `tfq` [TensorFlow Quantum](https://www.tensorflow.org/quantum)
- `qsharp` [QSharp](https://docs.microsoft.com/en-us/quantum/language/index?view=qsharp-preview)
- `quest` [QuEST](https://quest.qtechtheory.org/)
- `js` [quantum-circuit](https://www.npmjs.com/package/quantum-circuit) (javascript)
- `quantum-circuit` [quantum-circuit](https://www.npmjs.com/package/quantum-circuit) (json)
- `toaster` [Qubit Toaster](https://quantastica.com/toaster/)
- `svg` [SVG (standalone)](https://www.w3.org/Graphics/SVG/)
- `svg-inline` [SVG (inline)](https://www.w3.org/Graphics/SVG/)
**Example 1** - convert QASM 2.0 program to QUIL:
```python
from quantastica.qps_api import QPS
input_program = """
OPENQASM 2.0;
include "qelib1.inc";
qreg q[2];
creg c[2];
h q[0];
cx q[0], q[1];
measure q[0] -> c[0];
measure q[1] -> c[1];
"""
output_program = QPS.converter.convert(input_program, "qasm", "quil")
print(output_program)
```
Output:
```
DECLARE ro BIT[2]
H 0
CNOT 0 1
MEASURE 0 ro[0]
MEASURE 1 ro[1]
```
**Example 2** - convert QASM 2.0 program to circuit drawing as vector image:
```python
from quantastica.qps_api import QPS
input_program = """
OPENQASM 2.0;
include "qelib1.inc";
qreg q[2];
creg c[2];
h q[0];
cx q[0], q[1];
measure q[0] -> c[0];
measure q[1] -> c[1];
"""
output_svg = QPS.converter.convert(input_program, "qasm", "svg")
# Do something with returned vector image...
open("output.svg", "w").write(output_svg)
```
## Utils API
**QPS.utils.random_circuit(num_qubits=5, output_format="quantum-circuit", options=None)**
Returns random quantum circuit.
- `num_qubits` Integer. Number of qubits. Default: `5`.
- `format` String. Output format. The same as `QPS.converter.convert()` function's `dest` argument. Example: `"qasm"`. Default: `"quantum-circuit"`.
- `options` Dict. Optional. Can contain following keys:
- `instruction_set` List of gates to use. Example: `["u3", "cx"]`. Default: `[ "u3", "rx", "ry", "rz", "cx", "cz" ]`.
- `num_gates` Integer. Number of gates in the circuit. Default is `num_qubits * 8`.
- `mid_circuit_measurement` Bool. Default: `False`.
- `mid_circuit_reset` Bool. Default: `False`.
- `classic_control` Bool. Default: `False`.
Raw data
{
"_id": null,
"home_page": "https://github.com/quantastica/qps-api",
"name": "quantastica-qps-api",
"maintainer": null,
"docs_url": null,
"requires_python": null,
"maintainer_email": null,
"keywords": null,
"author": "Quantastica",
"author_email": "support@quantastica.com",
"download_url": "https://files.pythonhosted.org/packages/18/4a/5906563c521f9a4aadef48837ec5fac1f52c0ec5d4ed5efde8687ac9673d/quantastica-qps-api-0.9.11.tar.gz",
"platform": null,
"description": "# Quantum Programming Studio API\n\nPython wrapper for [Quantum Programming Studio](https://quantum-circuit.com/) HTTP API.\n\n\n## Install and configure\n\n**1. Install QPS API package:**\n\n```bash\npip install quantastica-qps-api\n```\n\n**2. Find your QPS API token:**\n\n[Login](https://quantum-circuit.com/login) to Quantum Programming Studio, go to [Profile -> API Access](https://quantum-circuit.com/user_settings/api) and copy your API token.\n\n**3. Configure QPS API package with your API token:**\n\n```python\n\nfrom quantastica.qps_api import QPS\n\nQPS.save_account(\"YOUR_API_TOKEN\")\n\n```\n\nThat will create a local configuration file where your API token will be stored for future use.\n\n**Now you are ready to use QPS API.**\n\n\n## Account management\n\n**QPS.save_account(api_token, api_url=None)**\n\n**Run this once** to setup your QPS REST API account. Method will create configuration file and your api token will be stored there for future use.\n\nIf needed, you can clear your token by running `QPS.save_account(\"\")` (or by deleting a configuration file).\n\nIf `api_url` is not provided then `https://quantum-circuit.com/api/` will be set as default.\n\n\n**QPS.config_path()**\n\nYou can get config file path by running `QPS.config_path()`.\n\nDefault configuration file path:\n\n- On Unix, directory is obtained from environment variable HOME if it is set; otherwise the current user\u2019s home directory is looked up in the password directory through the built-in module pwd.\n\n- On Windows, USERPROFILE will be used if set, otherwise a combination of HOMEPATH and HOMEDRIVE will be used.\n\n\n\n## Synthesis and transpilation API\n\nSynthesis and transpilation tool can be used to:\n\n- create quantum circuit from state vectors\n\n- create quantum circuit from truth table\n\n- create quantum circuit from unitary matrix (decompose unitary matrix)\n\n- transpile circuits (change instruction set)\n\n\n### Circuit from vectors\n\nFind quantum circuit from pairs of initial & final state vectors (wave functions).\n\n**QPS.synth.circuit_from_vectors(vector_pairs, endianness = \"little\", job_name=None, settings = {}, start_job=True)**\n\n- `vector_pairs` is list containing vector pairs. Each vector pair is list with 2 elements: initial vector and final vector. All vectors in all pairs must be of same length (same number of qubits).\n\n- `endianness` string. Orientation of bits in state vector (most significant bit/first qubit or least significant bit/first qubit). Can be `little` (like Qiskit) or `big`. Default is `little`.\n\n- `job_name` string is optional. You can give it a human readable name.\n\n- `start_job` if this argument is `True` (default) the job will be immediatelly sent to execution queue. If `start_job` is `False` then it will stay in `draft` state and you will be able to start it later by calling `start_job()` method.\n\n- `settings` object is optional. Default is:\n\n```python\n\n{\n\t\"strategy\": \"strategy_a\",\n\t\"pre_processing\": \"\",\n\t\"allowed_gates\": \"u3,cx\",\n\t\"min_gates\": 0,\n\t\"max_gates\": 0,\n\t\"max_diff\": 0.001,\n\t\"diff_method\": \"ignorephase\",\n\t\"max_duration\": 0,\n\t\"single_solution\": True\n}\n\n```\n\n**Settings**\n\n- `strategy` string. Can be one of: `strategy_a` (brute force or heuristics) and `strategy_b` OptigenQ+QSD (OptigenQ is Quantastica's method to find unitary matrix from pairs of vectors, and QSD is used to decompose matrix and return circuit). Default is `strategy_a`.\n\n- `pre_processing` string. Used only with `strategy_a` (brute force or heuristics). Can be one of: `stable`, `experimental_1`, `experimental_2`, `experimental_3`, `experimental_5`. Default is empty string (Empty string means \"use default\" which is currently `stable`).\n\n- `allowed_gates` string. Used only with `strategy_a` (brute force or heuristics). Comma delimited gate names (instruction set) to use. Default is `u3,cx`. With `strategy_b` instruction set is fixed to `u3,cx` (you can transpile returned circuit later using `QPS.synth.transpile()` method.\n\n- `min_gates` and `max_gates` integer. Used only with `strategy_a` (brute force or heuristics). Default is `0` (no limits).\n\n- `max_diff` float. Used only with `strategy_a` (brute force or heuristics). Default is `0.001` (1e-3).\n\n- `diff_method` string. Used only with `strategy_a` (brute force or heuristics). Can be one of: `distance` (exact match), `ignorephase` (match up to a global phase) and `abs` (match absolute values). Default is `ignorephase`.\n\n- `max_duration` integer. Timeout in seconds. Solver will stop after specified number of seconds and error will be returned. Useful with brute force algorithm which has high computational complexity and can run for a very long time. You can decide when to stop (and for example proceed with the next job which is using different method and is waiting in a queue - if you prepared it). Default is `0` which means \"maximum allowed by your subscription plan\". Free plan has limit of a few seconds (subject to change).\n\n- `single_solution` boolean. Used only with `strategy_a` (brute force or heuristics). When `True`, solver will stop when first solution was found. When `False` solver will return all possible configurations of a circuit.\n\nNote: if `settings` argument is provided, it will overwrite default settings, but only provided keys will be overwritten - not entire default settings object.\n\n\n**Example:**\n\n```python\n\nfrom quantastica.qps_api import QPS\n\nvector_pairs = [\n[ [1, 0, 0, 0], [ 0.5+0j, 0.5+0j, 0.5+0j, 0.5+0j ] ],\n[ [0, 1, 0, 0], [ 0.5+0j, 0+0.5j, -0.5+0j, 0-0.5j ] ],\n[ [0, 0, 1, 0], [ 0.5+0j, -0.5+0j, 0.5+0j, -0.5+0j ] ],\n[ [0, 0, 0, 1], [ 0.5+0j, 0-0.5j, -0.5+0j, 0+0.5j ] ]\n]\n\njob_id = QPS.synth.circuit_from_vectors(vector_pairs, settings = { \"instruction_set\": [\"h\", \"cu1\", \"swap\"], \"single_solution\": False })\n\njob = QPS.synth.get_job(job_id, wait=True)\n\njob_status = job[\"status\"]\njob_output = job[\"output\"]\n\nif(job_status == \"error\"):\n\tprint(job_output[\"message\"])\nelse:\n\tfor circuit in job_output[\"circuits\"]:\n\t\tprint(circuit[\"qasm\"])\n\n```\n\nExample output:\n\n```\nOPENQASM 2.0;\ninclude \"qelib1.inc\";\nqreg q[2];\nh q[1];\nswap q[0], q[1];\ncu1 (2.356194496154785) q[0], q[1];\nh q[1];\n\nOPENQASM 2.0;\ninclude \"qelib1.inc\";\nqreg q[2];\nh q[1];\ncu1 (2.356194496154785) q[0], q[1];\nh q[0];\nswap q[0], q[1];\n\nOPENQASM 2.0;\ninclude \"qelib1.inc\";\nqreg q[2];\nh q[1];\ncu1 (2.356194496154785) q[0], q[1];\nswap q[0], q[1];\nh q[1];\n\nOPENQASM 2.0;\ninclude \"qelib1.inc\";\nqreg q[2];\nswap q[0], q[1];\nh q[0];\ncu1 (2.356194496154785) q[0], q[1];\nh q[1];\n```\n\n### State preparation\n\nGet circuit which prepares provided state.\n\n**QPS.synth.state_preparation(final_vector, endianness = \"little\", job_name=None, settings = {}, start_job=True)**\n\n- `final_vector` is target vector.\n\n- `endianness` string. Orientation of bits in state vector (most significant bit/first qubit or least significant bit/first qubit). Can be `little` (like Qiskit) or `big`. Default is `little`.\n\n- `job_name` string is optional. You can give it a human readable name.\n\n- `start_job` if this argument is `True` (default) the job will be immediatelly sent to execution queue. If `start_job` is `False` then it will stay in `draft` state and you will be able to start it later by calling `start_job()` method.\n\n- `settings` object is optional. Default is:\n\n```python\n\n{\n\t\"strategy\": \"strategy_a\",\n\t\"pre_processing\": \"\",\n\t\"allowed_gates\": \"u3,cx\",\n\t\"min_gates\": 0,\n\t\"max_gates\": 0,\n\t\"max_diff\": 0.001,\n\t\"diff_method\": \"ignorephase\",\n\t\"max_duration\": 0,\n\t\"single_solution\": True\n}\n\n```\n\n**Settings**\n\n- `strategy` string. Can be one of: `strategy_a` (brute force or heuristics) and `strategy_b` OptigenQ+QSD (OptigenQ is Quantastica's method to find unitary matrix from pairs of vectors, after which QSD is used to decompose matrix and return circuit). Default is `strategy_a`.\n\n- `pre_processing` string. Used only with `strategy_a` (brute force or heuristics). Can be one of: `stable`, `experimental_1`, `experimental_2`, `experimental_3`, `experimental_5`. Default is empty string (Empty string means \"use default\" which is currently `stable`).\n\n- `allowed_gates` string. Used only with `strategy_a` (brute force or heuristics). Comma delimited gate names (instruction set) to use. Default is `u3,cx`. With `strategy_b` instruction set is fixed to `u3,cx` (you can transpile returned circuit later using `QPS.synth.transpile()` method.\n\n- `min_gates` and `max_gates` integer. Used only with `strategy_a` (brute force or heuristics). Default is `0` (no limits).\n\n- `max_diff` float. Used only with `strategy_a` (brute force or heuristics). Default is `0.001` (1e-3).\n\n- `diff_method` string. Used only with `strategy_a` (brute force or heuristics). Can be one of: `distance` (exact match), `ignorephase` (match up to a global phase) and `abs` (match absolute values). Default is `ignorephase`.\n\n- `max_duration` integer. Timeout in seconds. Solver will stop after specified number of seconds and error will be returned. Useful with brute force algorithm which has high computational complexity and can run for a very long time. You can decide when to stop (and for example proceed with the next job which is using different method and is waiting in a queue - if you prepared it). Default is `0` which means \"maximum allowed by your subscription plan\". Free plan has limit of a few seconds (subject to change).\n\n- `single_solution` boolean. Used only with `strategy_a` (brute force or heuristics). When `True`, solver will stop when first solution was found. When `False` solver will return all possible configurations of a circuit.\n\nNote: if `settings` argument is provided, it will overwrite default settings, but only provided keys will be overwritten - not entire default settings object.\n\n\n**Example:**\n\n```python\n\nfrom quantastica.qps_api import QPS\n\ndesired_state = [0.5, 0.5, 0.5, 0.5]\n\njob_id = QPS.synth.state_preparation(desired_state, settings = { \"instruction_set\": [\"u3\", \"cx\"] })\n\njob = QPS.synth.get_job(job_id, wait=True)\n\njob_status = job[\"status\"]\njob_output = job[\"output\"]\n\nif(job_status == \"error\"):\n\tprint(job_output[\"message\"])\nelse:\n\tfor circuit in job_output[\"circuits\"]:\n\t\tprint(circuit[\"qasm\"])\n\n```\n\nExample output:\n\n```\nOPENQASM 2.0;\ninclude \"qelib1.inc\";\nqreg q[2];\nu3 (1.570796370506287, 0, 1.217840194702148) q[0];\nu3 (1.570796370506287, 0, 0.621559917926788) q[1];\n```\n\n\n### Transpile\n\nTranspile circuit (change instruction set).\n\n**QPS.synth.transpile(input_qasm, method=\"replace_blocks\", method_options={}, job_name=None, settings = {}, start_job=True)**\n\n- `input_qasm` is string containing OpenQASM 2.0 code.\n\n- `method` is method name string. Can be one of: \"replace_circuit\", \"replace_blocks\", \"replace_gates\". Default: \"replace_blocks\".\n\n- `method_options` dict with following structure:\n\n\t- If method is `replace_blocks` then: `{ \"block_size\": 2, \"two_pass\": False }` (maximum block size is 4).\n\n\t- For other methods: no options (`method_options` is ignored)\n\n- `job_name` string is optional. You can give it a human readable name.\n\n- `start_job` if this argument is `True` (default) the job will be immediatelly sent to execution queue. If `start_job` is `False` then it will stay in `draft` state and you will be able to start it later by calling `start_job()` method.\n\n- `settings` object is optional. Default is:\n\n```python\n\n{\n\t\"pre_processing\": \"experimental1\",\n\t\"allowed_gates\": \"u3,cx\",\n\t\"max_diff\": 0.001,\n\t\"diff_method\": \"ignorephase\",\n\t\"max_duration\": 0\n}\n\n```\n\n**Settings**\n\n- `pre_processing` string. Can be one of: `stable`, `experimental_1`, `experimental_2`, `experimental_3`, `experimental_5`. Default is empty string (Empty string means \"use default\" which is currently `stable`).\n\n- `allowed_gates` string. Comma delimited gate names (instruction set) to use. Default is `u3,cx`.\n\n- `max_diff` float. Default is `0.001` (1e-3).\n\n- `diff_method` string. Can be one of: `distance` (exact match), `ignorephase` (match up to a global phase) and `abs` (match absolute values). Default is `ignorephase`.\n\n- `max_duration` integer. Timeout in seconds. Solver will stop after specified number of seconds and error will be returned. Default is `0` which means \"maximum allowed by your subscription plan\". Free plan has limit of a few seconds (subject to change).\n\nNote: if `settings` argument is provided, it will overwrite default settings, but only provided keys will be overwritten - not entire default settings object.\n\n\n**Example:**\n\n```python\n\nfrom quantastica.qps_api import QPS\n\ninput_qasm = \"\"\"\nOPENQASM 2.0;\ninclude \"qelib1.inc\";\nqreg q[2];\nh q[0];\ncx q[0], q[1];\n\"\"\"\n\njob_id = QPS.synth.transpile(input_qasm, settings = { \"instruction_set\": [\"id\", \"x\", \"sx\", \"rz\", \"cx\"], \"diff_method\": \"ignorephase\" })\n\njob = QPS.synth.get_job(job_id, wait=True)\n\njob_status = job[\"status\"]\njob_output = job[\"output\"]\n\nif(job_status == \"error\"):\n\tprint(job_output[\"message\"])\nelse:\n\tfor circuit in job_output[\"circuits\"]:\n\t\tprint(circuit[\"qasm\"])\n\n```\n\nExample output:\n\n```\nOPENQASM 2.0;\ninclude \"qelib1.inc\";\nqreg q[2];\nsx q[0];\nrz (1.570796370506287) q[0];\nsx q[0];\ncx q[0], q[1];\n```\n\n### Decompose matrix\n\nDecompose unitary matrix (find circuit from matrix).\n\n**QPS.synth.decompose_unitary(unitary, endianness=\"big\", job_name=None, settings = {}, start_job=True)**\n\n- `unitary` matrix operator.\n\n- `endianness` - orientation of the matrix. Can be `little` endian (like Qiskit) or `big` endian. Default is `big`. Note that default endianness of the matrix differs from default endianness of vectors in other methods. That's to be aligned with QPS. In Qiskit, both matrices and vectors are `little` endian. So, if you are solving unitary from Qiskit then provide `endianness = \"little\"` argument.\n\n- `job_name` string is optional. You can give it a human readable name.\n\n- `start_job` if this argument is `True` (default) the job will be immediatelly sent to execution queue. If `start_job` is `False` then it will stay in `draft` state and you will be able to start it later by calling `start_job()` method.\n\n- `settings` object is optional. Default is:\n\n```python\n\n{\n\t\"strategy\": \"strategy_a\",\n\t\"pre_processing\": \"\",\n\t\"allowed_gates\": \"u3,cx\",\n\t\"coupling_map\": [],\n\t\"min_gates\": 0,\n\t\"max_gates\": 0,\n\t\"max_diff\": 0.001,\n\t\"diff_method\": \"ignorephase\",\n\t\"max_duration\": 0,\n\t\"single_solution\": True\n}\n\n```\n\n**Settings**\n\n- `strategy` string. Can be one of: `strategy_a` (brute force or heuristics) and `strategy_b` QSD. Default is `strategy_a`. Note that `strategy_a` (brute force or heuristics) returns circuit with optimal or near optimal depth, but this method has very high computational complexity and can run for a very long time (depending on number of qubits and total number of gates in the resulting circuit).\n\n- `pre_processing` string. Used only with `strategy_a` (brute force or heuristics). Can be one of: `stable`, `experimental_1` and `experimental_5`. Default is empty string (Empty string means \"use default\" which is currently `stable`). Experimental methods have lower computational complexity and should finish sooner, but that depends on many factors, so you should try all methods and see what works best for your particular problem.\n\n- `allowed_gates` string. Used only with `strategy_a` (brute force or heuristics). Comma delimited gate names (instruction set) to use. Default is `u3,cx`. With `strategy_b` instruction set is fixed to `u3,cx` (you can transpile returned circuit later using `QPS.synth.transpile()` method.\n\n- `min_gates` and `max_gates` integer. Used only with `strategy_a` (brute force or heuristics). Default is `0` (no limits).\n\n- `max_diff` float. Used only with `strategy_a` (brute force or heuristics). Default is `0.001` (1e-3).\n\n- `diff_method` string. Used only with `strategy_a` (brute force or heuristics). Can be one of: `distance` (exact match), `ignorephase` (match up to a global phase) `hs` (match up to a global phase using faster method) and `abs` (match absolute values). Default is `ignorephase`.\n\n- `max_duration` integer. Timeout in seconds. Solver will stop after specified number of seconds and error will be returned. Useful with brute force algorithm which has high computational complexity and can run for a very long time. You can decide when to stop (and for example proceed with the next job which is using different method and is waiting in a queue - if you prepared it). Default is `0` which means \"maximum allowed by your subscription plan\". Free plan has limit of a few seconds (subject to change).\n\n- `single_solution` boolean. Used only with `strategy_a` (brute force or heuristics). When `True`, solver will stop when first solution was found. When `False` solver will return all possible configurations of a circuit.\n\nNote: if `settings` argument is provided, it will overwrite default settings, but only provided keys will be overwritten - not entire default settings object.\n\n\n**Example:**\n\n```python\n\nfrom quantastica.qps_api import QPS\n\nunitary = [\n[ 0.5+0.0j, 0.5+0.0j, 0.5+0.0j, 0.5+0.0j],\n[ 0.5+0.0j, 0.5+0.0j, -0.5+0.0j, -0.5+0.0j],\n[ 0.5+0.0j, -0.5+0.0j, 0.0+0.5j, 0.0-0.5j],\n[ 0.5+0.0j, -0.5+0.0j, 0.0-0.5j, 0.0+0.5j]\n]\n\njob_id = QPS.synth.decompose_unitary(unitary, settings = { \"instruction_set\": [\"h\", \"cu1\", \"swap\"], \"single_solution\": False })\n\njob = QPS.synth.get_job(job_id, wait=True)\n\njob_status = job[\"status\"]\njob_output = job[\"output\"]\n\nif(job_status == \"error\"):\n\tprint(job_output[\"message\"])\nelse:\n\tfor circuit in job_output[\"circuits\"]:\n\t\tprint(circuit[\"qasm\"])\n\n```\n\nExample output:\n\n```\nOPENQASM 2.0;\ninclude \"qelib1.inc\";\nqreg q[2];\nh q[1];\nswap q[0], q[1];\ncu1 (1.570796370506287) q[0], q[1];\nh q[1];\n\nOPENQASM 2.0;\ninclude \"qelib1.inc\";\nqreg q[2];\nh q[1];\ncu1 (1.570796370506287) q[0], q[1];\nh q[0];\nswap q[0], q[1];\n\nOPENQASM 2.0;\ninclude \"qelib1.inc\";\nqreg q[2];\nh q[1];\ncu1 (1.570796370506287) q[0], q[1];\nswap q[0], q[1];\nh q[1];\n\nOPENQASM 2.0;\ninclude \"qelib1.inc\";\nqreg q[2];\nswap q[0], q[1];\nh q[0];\ncu1 (1.570796370506287) q[0], q[1];\nh q[1];\n```\n\n\n### Create algorithm from truth table\n\nCreate circuit which implements logical expression whose truth table is given.\n\n**QPS.synth.circuit_from_truth_table(truth_table_csv, column_defs, csv_delimiter=None, additional_qubits=1, job_name=None, settings={}, start_job=True)**\n\n- `truth_table_csv` is string containing truth table in CSV format\n\n- `column_defs` list of strings describing each column from truth table: `\"input\"`, `\"output\"` or `\"ignore\"`\n\n- `csv_delimiter` CSV column delimiter char: `None`, `\",\"` (comma) or `\"\\t\"` (tab). If delimiter is `None` (default) it will be automatically detected.\n\n- `additional_qubits` number of qubits to add (to displace input and output qubits).\n\n- `job_name` string is optional. You can give it a human readable name.\n\n- `settings` object is optional. Default is:\n\n```python\n\n{\n\t\"pre_processing\": \"\",\n\t\"allowed_gates\": \"x,cx,ccx,swap\",\n\t\"coupling_map\": [],\n\t\"min_gates\": 0,\n\t\"max_gates\": 0,\n\t\"max_diff\": 0.001,\n\t\"diff_method\": \"ignorephase\",\n\t\"max_duration\": 0,\n\t\"single_solution\": True\n}\n\n```\n\n**Settings**\n\n- `pre_processing` string. Can be one of: `stable`, `experimental_1` and `experimental_5`. Default is empty string (Empty string means \"use default\" which is currently `stable`).\n\n- `allowed_gates` string. Comma delimited gate names (instruction set) to use. Default is `x,cx,ccx,swap`.\n\n- `min_gates` and `max_gates` integer. Default is `0` (no limits).\n\n- `max_diff` float. Used only with `strategy_a` (brute force or heuristics). Default is `0.001` (1e-3).\n\n- `diff_method` string. Used only with `strategy_a` (brute force or heuristics). Can be one of: `distance` (exact match), `ignorephase` (match up to a global phase) and `abs` (match absolute values). Default is `ignorephase`.\n\n- `max_duration` integer. Timeout in seconds. Solver will stop after specified number of seconds and error will be returned. Useful with brute force algorithm which has high computational complexity and can run for a very long time. You can decide when to stop (and for example proceed with the next job which is using different method and is waiting in a queue - if you prepared it). Default is `0` which means \"maximum allowed by your subscription plan\". Free plan has limit of a few seconds (subject to change).\n\n- `single_solution` boolean. Used only with `strategy_a` (brute force or heuristics). When `True`, solver will stop when first solution was found. When `False` solver will return all possible configurations of a circuit.\n\nNote: if `settings` argument is provided, it will overwrite default settings, but only provided keys will be overwritten - not entire default settings object.\n\n\n**Example:**\n\n```python\n\nfrom quantastica.qps_api import QPS\n\ntruth_table = \"\"\"\nA,B,A_NAND_B\n0,0,1\n0,1,1\n1,0,1\n1,1,0\n\"\"\"\n\njob_id = QPS.synth.circuit_from_truth_table(truth_table, [\"input\", \"input\", \"output\"])\n\njob = QPS.synth.get_job(job_id, wait=True)\n\njob_status = job[\"status\"]\njob_output = job[\"output\"]\n\nif(job_status == \"error\"):\n\traise Exception(job_output[\"message\"])\nelse:\n\tif(len(job_output[\"circuits\"]) == 0):\n\t\traise Exception(\"No results.\")\n\telse:\n\t\tfor circuit in job_output[\"circuits\"]:\n\t\t\tprint(circuit[\"qasm\"])\n\n```\n\nExample output:\n\n```\nOPENQASM 2.0;\ninclude \"qelib1.inc\";\nqreg q[3];\nx q[2];\nccx q[0], q[1], q[2];\n```\n\n\n### Run problem file\n\nSolve problem provided in internal format used by synthesizer and transpiler.\n\n**QPS.synth.solve(problem, settings = {}, start_job=True)**\n\n- `problem` object (e.g. job exported to json from Quantum Programming studio).\n\n- `settings` argument is optional. If provided, it will overwrite keys in `problem.settings`. Note that only provided keys will be overwritten - not entire `problem.settings` object.\n\n- `start_job` if this argument is `True` (default) the job will be immediatelly sent to execution queue. If `start_job` is `False` then it will stay in `draft` state and you will be able to start it later by calling `start_job()` method.\n\n\n**Example:**\n\n```python\n\nfrom quantastica.qps_api import QPS\n\nproblem = {\n\t\"name\": \"Bell\",\n\t\"type\": \"vectors\",\n\t\"source\": {\n\t\t\"vectors\": {\n\t\t\t\"text1\": \"[ 1, 0, 0, 0 ]\",\n\t\t\t\"text2\": \"[ 1/sqrt(2), 0, 0, 1/sqrt(2) ]\",\n\t\t\t\"endianness1\": \"little\",\n\t\t\t\"endianness2\": \"little\"\n\t\t}\n\t},\n\t\"problem\": [\n\t\t{\n\t\t\t\"input\": [\n\t\t\t\t1,\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t\t0\n\t\t\t],\n\t\t\t\"output\": [\n\t\t\t\t0.7071067811865475,\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t\t0.7071067811865475\n\t\t\t]\n\t\t}\n\t],\n\t\"settings\": {\n\t\t\"strategy\": \"strategy_a\",\n\t\t\"pre_processing\": \"\",\n\t\t\"allowed_gates\": \"u3,cx\",\n\t\t\"coupling_map\": [],\n\t\t\"min_gates\": 0,\n\t\t\"max_gates\": 0,\n\t\t\"max_diff\": 0.001,\n\t\t\"diff_method\": \"distance\",\n\t\t\"max_duration\": 0,\n\t\t\"single_solution\": False\n\t}\n}\n\njob_id = QPS.synth.solve(problem)\n\njob = QPS.synth.get_job(job_id, wait=True)\n\njob_status = job[\"status\"]\njob_output = job[\"output\"]\n\nif(job_status == \"error\"):\n\tprint(job_output[\"message\"])\nelse:\n\tfor circuit in job_output[\"circuits\"]:\n\t\tprint(circuit[\"qasm\"])\n\n```\n\nExample output:\n\n```\nOPENQASM 2.0;\ninclude \"qelib1.inc\";\nqreg q[2];\nu3 (-1.570796370506287, -3.141592741012573, -2.675650835037231) q[0];\ncx q[0], q[1];\n\nOPENQASM 2.0;\ninclude \"qelib1.inc\";\nqreg q[2];\nu3 (-1.570796370506287, -3.141592741012573, -2.675650835037231) q[1];\ncx q[1], q[0];\n```\n\n\n### Synthesizer/transpiler output format\n\nFinished job object has following structure:\n\n```javascript\n{\n\t\"name\" : String,\n\t\"type\" : String,\n\t\"source\" : Object,\n\t\"problem\" : Array,\n\t\"settings\" : Object,\n\t\"status\" : String,\n\t\"output\" : Object,\n\t\"queuedAt\" : String,\n\t\"startedAt\" : String,\n\t\"finishedAt\" : String\n}\n```\n\nKeys important to user are:\n\n- `name` String: name of the job\n\n- `status` String: can be `draft`, `queued`, `running`, `error`, `done`.\n\n- `output` Object with following structure:\n\n```javascript\n{\n\t\"error_code\" : Integer,\n\t\"message\" : String,\n\t\"time_taken\" : Float,\n\t\"version\" : String,\n\t\"circuits\" : Array of Object\n}\n```\n\n- `error_code` Integer: 0 on success, non-zero on error\n\n- `message` String: error message if error code is non-zero\n\n- `time_taken` Float: number of seconds\n\n- `version` String: solver version\n\n- `circuits` Array: resulting circuits. Each is object with following structure:\n\n```javascript\n{\n\t\"qubits\" : Integer,\n\t\"cregs\" : Array,\n\t\"program\" : Array,\n\t\"diff\" : Float,\n\t\"index\" : Integer,\n\t\"qasm\" : String,\n\t\"qasmExt\" : String\n}\n```\n\nKeys important to user are:\n\n- `qasm` OpenQASM 2.0 source code of the resulting circuit.\n\n- `qasmExt` OpenQASM 2.0 with extended instruction set (all gates supported by Quantum Programming Studio).\n\nDifference between `qasm` and `qasmExt`: if circuit contains gate supported by QPS but not directly supported by OpenQASM 2.0 then `qasm` will contain equivalent circuit transpiled to OpenQASM 2.0 instruction set, but `qasmExt` will contain gates as is.\n\nFor example if circuit contains IONQ native gate `gpi2(2.51678906856393)` on first qubit:\n\n`qasm` will be:\n\n```\nOPENQASM 2.0;\ninclude \"qelib1.inc\";\nqreg q[1];\nu3 (1.5707963267948966, 0.9459927417690333, -0.9459927417690333) q[0];\n```\n\n`qasmExt` will contain:\n```\nOPENQASM 2.0;\ninclude \"qelib1.inc\";\nqreg q[1];\ngpi2 (2.51678906856393) q[0];\n```\n\n\n**Example job object with output:**\n\n```python\n\n{\n\t\"_id\": \"r9LskFoLPQW5w7HTp\",\n\t\"name\": \"Bell\",\n\t\"type\": \"vectors\",\n\t\"source\": {\n\t\t\"vectors\": {\n\t\t\t\"text1\": \"[ 1, 0, 0, 0 ]\",\n\t\t\t\"text2\": \"[ 1/sqrt(2), 0, 0, 1/sqrt(2) ]\",\n\t\t\t\"endianness1\": \"little\",\n\t\t\t\"endianness2\": \"little\"\n\t\t}\n\t},\n\t\"problem\": [\n\t\t{\n\t\t\t\"input\": [\n\t\t\t\t1,\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t\t0\n\t\t\t],\n\t\t\t\"output\": [\n\t\t\t\t0.7071067811865475,\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t\t0.7071067811865475\n\t\t\t]\n\t\t}\n\t],\n\t\"settings\": {\n\t\t\"strategy\": \"strategy_a\",\n\t\t\"pre_processing\": \"\",\n\t\t\"allowed_gates\": \"u3,cx\",\n\t\t\"coupling_map\": [],\n\t\t\"min_gates\": 0,\n\t\t\"max_gates\": 0,\n\t\t\"max_diff\": 0.001,\n\t\t\"diff_method\": \"distance\",\n\t\t\"max_duration\": 0,\n\t\t\"single_solution\": False\n\t},\n\t\"output\": {\n\t\t\"circuits\": [\n\t\t\t{\n\t\t\t\t\"qubits\": 2,\n\t\t\t\t\"cregs\": [],\n\t\t\t\t\"diff\": 0,\n\t\t\t\t\"program\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"u3\",\n\t\t\t\t\t\t\"wires\": [\n\t\t\t\t\t\t\t0\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"options\": {\n\t\t\t\t\t\t\t\"params\": {\n\t\t\t\t\t\t\t\t\"theta\": 1.570796326794896,\n\t\t\t\t\t\t\t\t\"phi\": 0,\n\t\t\t\t\t\t\t\t\"lambda\": 1.456034103897321\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"cx\",\n\t\t\t\t\t\t\"wires\": [\n\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t\t1\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"options\": {}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"index\": 0\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"qubits\": 2,\n\t\t\t\t\"cregs\": [],\n\t\t\t\t\"diff\": 0,\n\t\t\t\t\"program\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"u3\",\n\t\t\t\t\t\t\"wires\": [\n\t\t\t\t\t\t\t1\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"options\": {\n\t\t\t\t\t\t\t\"params\": {\n\t\t\t\t\t\t\t\t\"theta\": 1.570796326794896,\n\t\t\t\t\t\t\t\t\"phi\": 0,\n\t\t\t\t\t\t\t\t\"lambda\": 1.456034103897321\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"cx\",\n\t\t\t\t\t\t\"wires\": [\n\t\t\t\t\t\t\t1,\n\t\t\t\t\t\t\t0\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"options\": {}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"index\": 1\n\t\t\t}\n\t\t],\n\t\t\"error_code\": 0,\n\t\t\"message\": \"\",\n\t\t\"time_taken\": 0.008,\n\t\t\"version\": \"0.1.0\",\n\t\t\"stats\": {\n\t\t\t\"result_count\": 2\n\t\t}\n\t},\n\t\"createdAt\": \"2021-02-06T23:39:29.108Z\",\n\t\"modifiedAt\": \"2021-02-06T23:39:30.383Z\",\n\t\"queuedAt\": \"2021-02-06T23:39:29.676Z\",\n\t\"startedAt\": \"2021-02-06T23:39:29.926Z\",\n\t\"finishedAt\": \"2021-02-06T23:39:30.383Z\",\n\t\"status\": \"done\"\n}\n\n```\n\n\n### Using synthesizer and transpiler with Qiskit\n\nFormat used for input and output is OpenQASM 2.0, so integration with Qiskit (and other frameworks that support OpenQASM) is easy.\n\n**Example** transpile Qiskit circuit:\n\n```python\nfrom qiskit import QuantumCircuit\nfrom qiskit.circuit.random import random_circuit\nfrom qiskit.quantum_info import Operator\n\nfrom quantastica.qps_api import QPS\n\n# Generate random Qiskit circuit\nqc = random_circuit(5, 5, measure=False)\n\n# Get QASM code\ninput_qasm = qc.qasm()\n\n# Transpile with QPS\njob_id = QPS.synth.transpile(input_qasm, settings = { \"instruction_set\": [\"id\", \"u3\", \"cx\"], \"diff_method\": \"ignorephase\" })\njob = QPS.synth.get_job(job_id, wait=True)\njob_status = job[\"status\"]\njob_output = job[\"output\"]\nif(job_status == \"error\"):\n raise Exception(job_output[\"message\"])\n\ntranspiled_circuit = job_output[\"circuits\"][0]\n\n# Get QASM code\ntranspiled_qasm = transpiled_circuit[\"qasm\"]\n\n# Create Qiskit circuit\ntranspiled_qc = QuantumCircuit.from_qasm_str(transpiled_qasm)\n\n# Show circuit\nprint(\"Depth:\", transpiled_qc.depth())\nprint(\"Ops:\", sum(j for i, j in transpiled_qc.count_ops().items()))\ndisplay(transpiled_qc.draw(output=\"mpl\"))\n\n```\n\n\n### Job management\n\nProblem sent to solver (synthesizer or transpiler) is called a \"job\". Each job has unique ID. Solver is resource intensive tool, so it is configured to execute only one job at a time. While solver is processing a job, other jobs are queued. When solver finishes a job, it takes the next one from the queue.\n\nAPI provides functions for job manipulation: you can list all jobs (filtered by status), stop running job, cancel queued jobs, stop/cancel all jobs, start previously canceled (draft) job, etc.\n\n**QPS.synth.list_jobs(status_filter=None)**\n\nList all jobs, optionally filtered by status.\n\n- `status_filter` String, optional. Can be: `draft`, `queued`, `running`, `error`, `done`.\n\n\n**Example 1** - list all (unfiltered) jobs:\n\n```python\n\nfrom quantastica.qps_api import QPS\n\njobs = QPS.synth.list_jobs()\n\nprint(jobs)\n\n```\n\nExample output:\n\n```python\n{\n\t\"list\": [\n\t\t{ \"_id\": \"r9LskFoLPQW5w7HTp\", \"name\": \"Bell state\", \"type\": \"vectors\", \"status\": \"done\" },\n\t\t{ \"_id\": \"R8tJH7XoZ233oTREy\", \"name\": \"4Q Gauss\", \"type\": \"vectors\", \"status\": \"queued\" },\n\t\t{ \"_id\": \"h7fzYbFz8MJvkNhiX\", \"name\": \"Challenge\", \"type\": \"unitary\", \"status\": \"draft\" },\n\t\t{ \"_id\": \"PC5PNXiGqhh2HmkX8\", \"name\": \"Experiment\", \"type\": \"vectors\", \"status\": \"error\"},\n\t\t{ \"_id\": \"SNhiCqSCT2WwRWKCd\", \"name\": \"Decompose\", \"type\": \"unitary\", \"status\": \"running\" }\n\t]\n}\n```\n\n**Example 2** - list `running` jobs:\n\n```python\n\nfrom quantastica.qps_api import QPS\n\njobs = QPS.synth.list_jobs(status_filter=\"running\")\n\nprint(jobs)\n\n```\n\nExample output:\n\n```python\n{\n\t\"list\": [\n\t\t{ \"_id\": \"SNhiCqSCT2WwRWKCd\", \"name\": \"Decompose\", \"type\": \"unitary\", \"status\": \"running\" }\n\t]\n}\n```\n\n\n**QPS.synth.job_status(job_id)**\n\nGet job status.\n\n**Example:**\n\n```python\n\nfrom quantastica.qps_api import QPS\n\nstatus = QPS.synth.job_status(\"PC5PNXiGqhh2HmkX8\")\n\nprint(status)\n\n```\n\nExample output:\n\n```python\n{ \"_id\": \"PC5PNXiGqhh2HmkX8\", \"name\": \"Experiment\", \"type\": \"vectors\", \"status\": \"error\", \"message\": \"connect ECONNREFUSED\" }\n```\n\n\n**QPS.synth.get_job(job_id, wait=True)**\n\nGet job referenced by ID. If `wait` argument is `True` (default), then function will wait for a job to finish (or fail) before returning. If `wait` is `False`, then job will be immediatelly returned even if it is still running (in which case it will not contain a solution).\n\n**Example:**\n\n```python\n\nfrom quantastica.qps_api import QPS\n\njob = QPS.synth.get_job(\"r9LskFoLPQW5w7HTp\")\n\nprint(job)\n\n```\n\nExample output:\n\n```python\n{\n\t\"_id\": \"r9LskFoLPQW5w7HTp\",\n\t\"name\": \"Bell\",\n\t\"type\": \"vectors\",\n\t\"source\": {\n\t\t\"vectors\": {\n\t\t\t\"text1\": \"[ 1, 0, 0, 0 ]\",\n\t\t\t\"text2\": \"[ 1/sqrt(2), 0, 0, 1/sqrt(2) ]\",\n\t\t\t\"endianness1\": \"little\",\n\t\t\t\"endianness2\": \"little\"\n\t\t}\n\t},\n\t\"problem\": [\n\t\t{\n\t\t\t\"input\": [ 1, 0, 0, 0 ],\n\t\t\t\"output\": [ 0.7071067811865475, 0, 0, 0.7071067811865475 ]\n\t\t}\n\t],\n\t\"settings\": {\n\t\t\"max_duration\": 0,\n\t\t\"allowed_gates\": \"u3,cx\",\n\t\t\"coupling_map\": [],\n\t\t\"min_gates\": 0,\n\t\t\"max_gates\": 0,\n\t\t\"pre_processing\": \"\",\n\t\t\"strategy\": \"strategy_a\",\n\t\t\"max_diff\": 0.001,\n\t\t\"diff_method\": \"distance\",\n\t\t\"single_solution\": False\n\t},\n\t\"status\": \"done\",\n\t\"output\": {\n\t\t\"circuits\": [\n\t\t\t{\n\t\t\t\t\"qubits\": 2,\n\t\t\t\t\"cregs\": [],\n\t\t\t\t\"diff\": 0,\n\t\t\t\t\"program\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"u3\",\n\t\t\t\t\t\t\"wires\": [ 0 ],\n\t\t\t\t\t\t\"options\": {\n\t\t\t\t\t\t\t\"params\": {\n\t\t\t\t\t\t\t\t\"theta\": -1.570796370506287,\n\t\t\t\t\t\t\t\t\"phi\": -3.141592741012573,\n\t\t\t\t\t\t\t\t\"lambda\": -5.327113628387451\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"cx\",\n\t\t\t\t\t\t\"wires\": [ 0, 1 ],\n\t\t\t\t\t\t\"options\": {}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"index\": 0,\n\t\t\t\t\"qasm\": \"OPENQASM 2.0;\\ninclude \\\"qelib1.inc\\\";\\nqreg q[2];\\nu3 (-1.570796370506287, -3.141592741012573, -5.327113628387451) q[0];\\ncx q[0], q[1];\\n\",\n\t\t\t\t\"qasmExt\": \"OPENQASM 2.0;\\ninclude \\\"qelib1.inc\\\";\\nqreg q[2];\\nu3 (-1.570796370506287, -3.141592741012573, -5.327113628387451) q[0];\\ncx q[0], q[1];\\n\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"qubits\": 2,\n\t\t\t\t\"cregs\": [],\n\t\t\t\t\"diff\": 0,\n\t\t\t\t\"program\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"u3\",\n\t\t\t\t\t\t\"wires\": [ 1 ],\n\t\t\t\t\t\t\"options\": {\n\t\t\t\t\t\t\t\"params\": {\n\t\t\t\t\t\t\t\t\"theta\": -1.570796370506287,\n\t\t\t\t\t\t\t\t\"phi\": -3.141592741012573,\n\t\t\t\t\t\t\t\t\"lambda\": -5.327113628387451\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"cx\",\n\t\t\t\t\t\t\"wires\": [ 1, 0 ],\n\t\t\t\t\t\t\"options\": {}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"index\": 1,\n\t\t\t\t\"qasm\": \"OPENQASM 2.0;\\ninclude \\\"qelib1.inc\\\";\\nqreg q[2];\\nu3 (-1.570796370506287, -3.141592741012573, -5.327113628387451) q[1];\\ncx q[1], q[0];\\n\",\n\t\t\t\t\"qasmExt\": \"OPENQASM 2.0;\\ninclude \\\"qelib1.inc\\\";\\nqreg q[2];\\nu3 (-1.570796370506287, -3.141592741012573, -5.327113628387451) q[1];\\ncx q[1], q[0];\\n\"\t\t\t\t\n\t\t\t}\n\t\t],\n\t\t\"error_code\": 0,\n\t\t\"message\": \"\",\n\t\t\"time_taken\": 0.002,\n\t\t\"version\": \"0.1.0\"\n\t},\n\t\"queuedAt\": \"2021-02-06T23:39:29.676Z\",\n\t\"startedAt\": \"2021-02-06T23:39:29.926Z\",\n\t\"finishedAt\": \"2021-02-06T23:39:30.383Z\"\n}\n```\n\n**QPS.synth.stop_job(job_id)**\n\nStop running or cancel queued job. Job will be put into `draft` state, and you can start it again later by calling `start_job()`.\n\n**Example:**\n\n```python\n\nfrom quantastica.qps_api import QPS\n\nresponse = QPS.synth.stop_job(\"SNhiCqSCT2WwRWKCd\")\n\nprint(response)\n\n```\n\nExample output:\n\n```python\n{ \"_id\": \"SNhiCqSCT2WwRWKCd\", message: \"OK\" }\n```\n\n\n**QPS.synth.stop_all_jobs(status_filter=None)**\n\nStop running job / cancel all queued jobs.\n\n- `status_filter` - you can stop only a running job by providing `status_filter=\"running\"` (after this, next job from the queue will be executed). Or, you can cancel all queued jobs by providing `status_filter=\"queued\"` (running job will not be affected - it will continue running).\n\n**Example 1** - stop running job and remove all jobs from queue:\n\n```python\n\nfrom quantastica.qps_api import QPS\n\nstopped = QPS.synth.stop_all_jobs()\n\nprint(stopped)\n\n```\n\nExample output:\n\n```python\n{\n\t\"stopped\": [ \n\t\t{ \"_id\": \"SNhiCqSCT2WwRWKCd\", \"name\": \"Decompose\", \"type\": \"unitary\" },\n\t\t{ \"_id\": \"R8tJH7XoZ233oTREy\", \"name\": \"4Q Gauss\", \"type\": \"vectors\" }\n\t]\n}\n```\n\n**Example 2** - stop only a running job. Next job from queue, if any, will start:\n\n```python\n\nfrom quantastica.qps_api import QPS\n\nstopped = QPS.synth.stop_all_jobs(status_filter=\"running\")\n\nprint(stopped)\n\n```\n\nExample output:\n\n```python\n{\n\t\"stopped\": [\n\t\t{ \"_id\": \"SNhiCqSCT2WwRWKCd\", \"name\": \"Decompose\", \"type\": \"unitary\" }\n\t]\n}\n```\n\n**Example 3** - cancel all queued jobs. Running job will not be affected:\n\n```python\n\nfrom quantastica.qps_api import QPS\n\nstopped = QPS.synth.stop_all_jobs(status_filter=\"queued\")\n\nprint(stopped)\n\n```\n\nExample output:\n\n```python\n{\n\t\"stopped\": [\n\t\t{ \"_id\": \"R8tJH7XoZ233oTREy\", \"name\": \"4Q Gauss\", \"type\": \"vectors\" }\n\t]\n}\n```\n\n\n**QPS.synth.start_job(job_id)**\n\nStart previously stopped/canceled job (can be any job with status `draft`).\n\n**Example:**\n\n```python\n\nfrom quantastica.qps_api import QPS\n\nresponse = QPS.synth.start_job(\"SNhiCqSCT2WwRWKCd\")\n\nprint(response)\n\n```\n\nExample output:\n\n```python\n{ \"_id\": \"SNhiCqSCT2WwRWKCd\", \"message\": \"OK\" }\n```\n\n\n## Quantum Language Converter API\n\n[Quantum Language Converter](https://quantastica.com/#converters) is a tool which converts quantum program between different quantum programming languages and frameworks. It is also available as a [q-convert](https://www.npmjs.com/package/q-convert) command line tool and as a web UI at [https://quantum-circuit.com/qconvert](https://quantum-circuit.com/qconvert).\n\nQPS has integrated quantum language converter API which you can access directly from python code:\n\n\n**QPS.converter.convert(input, source, dest)**\n\nConverts `input` quantum program given as string from `source` format into `dest` format.\n\n- `input` String. Program source code\n\n- `source` String. Input format:\n\n\t- `qasm` [OpenQASM 2.0](https://github.com/Qiskit/openqasm) source code\n\t- `quil` [Quil](https://arxiv.org/abs/1608.03355) source code\n\t- `qobj` [QObj](https://arxiv.org/abs/1809.03452)\n\t- `ionq` [IONQ](https://docs.ionq.com/) (json)\n\t- `quantum-circuit` [quantum-circuit](https://www.npmjs.com/package/quantum-circuit) object (json)\n\t- `toaster` [Qubit Toaster](https://quantastica.com/#toaster) object (json)\n\n- `dest` String. Output format:\n\n\t- `qiskit` [Qiskit](https://qiskit.org/documentation/)\n\t- `qasm` [OpenQASM 2.0](https://github.com/Qiskit/openqasm)\n\t- `qasm-ext` OpenQASM 2.0 with complete instruction set supported by QPS (and other Quantastica tools)\n\t- `qobj` [QObj](https://arxiv.org/abs/1809.03452)\n\t- `quil` [Quil](https://arxiv.org/abs/1608.03355)\n\t- `pyquil` [pyQuil](http://docs.rigetti.com/en/latest/index.html)\n\t- `braket` [Braket](https://docs.aws.amazon.com/braket/)\n\t- `cirq` [Cirq](https://github.com/quantumlib/Cirq)\n\t- `tfq` [TensorFlow Quantum](https://www.tensorflow.org/quantum)\n\t- `qsharp` [QSharp](https://docs.microsoft.com/en-us/quantum/language/index?view=qsharp-preview)\n\t- `quest` [QuEST](https://quest.qtechtheory.org/)\n\t- `js` [quantum-circuit](https://www.npmjs.com/package/quantum-circuit) (javascript)\n\t- `quantum-circuit` [quantum-circuit](https://www.npmjs.com/package/quantum-circuit) (json)\n\t- `toaster` [Qubit Toaster](https://quantastica.com/toaster/)\n\t- `svg` [SVG (standalone)](https://www.w3.org/Graphics/SVG/)\n\t- `svg-inline` [SVG (inline)](https://www.w3.org/Graphics/SVG/)\n\n\n\n**Example 1** - convert QASM 2.0 program to QUIL:\n\n```python\nfrom quantastica.qps_api import QPS\n\ninput_program = \"\"\"\nOPENQASM 2.0;\ninclude \"qelib1.inc\";\nqreg q[2];\ncreg c[2];\nh q[0];\ncx q[0], q[1];\nmeasure q[0] -> c[0];\nmeasure q[1] -> c[1];\n\"\"\"\n\noutput_program = QPS.converter.convert(input_program, \"qasm\", \"quil\")\n\nprint(output_program)\n\n```\n\nOutput:\n\n```\nDECLARE ro BIT[2]\nH 0\nCNOT 0 1\nMEASURE 0 ro[0]\nMEASURE 1 ro[1]\n```\n\n\n**Example 2** - convert QASM 2.0 program to circuit drawing as vector image:\n\n```python\nfrom quantastica.qps_api import QPS\n\ninput_program = \"\"\"\nOPENQASM 2.0;\ninclude \"qelib1.inc\";\nqreg q[2];\ncreg c[2];\nh q[0];\ncx q[0], q[1];\nmeasure q[0] -> c[0];\nmeasure q[1] -> c[1];\n\"\"\"\n\noutput_svg = QPS.converter.convert(input_program, \"qasm\", \"svg\")\n\n# Do something with returned vector image...\nopen(\"output.svg\", \"w\").write(output_svg)\n\n```\n\n\n## Utils API\n\n**QPS.utils.random_circuit(num_qubits=5, output_format=\"quantum-circuit\", options=None)**\n\nReturns random quantum circuit.\n\n- `num_qubits` Integer. Number of qubits. Default: `5`.\n\n- `format` String. Output format. The same as `QPS.converter.convert()` function's `dest` argument. Example: `\"qasm\"`. Default: `\"quantum-circuit\"`.\n\n- `options` Dict. Optional. Can contain following keys:\n\n\t- `instruction_set` List of gates to use. Example: `[\"u3\", \"cx\"]`. Default: `[ \"u3\", \"rx\", \"ry\", \"rz\", \"cx\", \"cz\" ]`.\n\t- `num_gates` Integer. Number of gates in the circuit. Default is `num_qubits * 8`.\n\t- `mid_circuit_measurement` Bool. Default: `False`.\n\t- `mid_circuit_reset` Bool. Default: `False`.\n\t- `classic_control` Bool. Default: `False`.\n\n",
"bugtrack_url": null,
"license": "Apache License 2.0",
"summary": "Quantastica Quantum Programming Studio API",
"version": "0.9.11",
"project_urls": {
"Homepage": "https://github.com/quantastica/qps-api"
},
"split_keywords": [],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "781a1bbd0a7143c269ee8f9141c61a17b3a7abc6dcb1458043aec5d11d7652ce",
"md5": "f0f45282d51b8e70e71d1b688431c3c5",
"sha256": "278fc17c41610b7367d6a143ef4f1cd4ca3c5ccdcd2d986a462d8f374a6bae7d"
},
"downloads": -1,
"filename": "quantastica_qps_api-0.9.11-py3-none-any.whl",
"has_sig": false,
"md5_digest": "f0f45282d51b8e70e71d1b688431c3c5",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": null,
"size": 17719,
"upload_time": "2024-05-02T00:53:45",
"upload_time_iso_8601": "2024-05-02T00:53:45.477114Z",
"url": "https://files.pythonhosted.org/packages/78/1a/1bbd0a7143c269ee8f9141c61a17b3a7abc6dcb1458043aec5d11d7652ce/quantastica_qps_api-0.9.11-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "184a5906563c521f9a4aadef48837ec5fac1f52c0ec5d4ed5efde8687ac9673d",
"md5": "ae9c0fc654088e44b4dce67827b5262f",
"sha256": "6e6fb4371a3204c28ab6b422f795e91f815fe08c6180c0717b15aa63dc37bd94"
},
"downloads": -1,
"filename": "quantastica-qps-api-0.9.11.tar.gz",
"has_sig": false,
"md5_digest": "ae9c0fc654088e44b4dce67827b5262f",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 30831,
"upload_time": "2024-05-02T00:53:47",
"upload_time_iso_8601": "2024-05-02T00:53:47.781889Z",
"url": "https://files.pythonhosted.org/packages/18/4a/5906563c521f9a4aadef48837ec5fac1f52c0ec5d4ed5efde8687ac9673d/quantastica-qps-api-0.9.11.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-05-02 00:53:47",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "quantastica",
"github_project": "qps-api",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "quantastica-qps-api"
}