const math = require('mathjs');
const helper = require('./helper');
const math = require('mathjs');
const helper = require('./helper');
Complex number i and -i for easy access
const i = math.complex(0, 1);
const negI = math.complex(0, -1);
/* #################################################
# Gates #
################################################# */
class gates {
Pauli-X / Not Gate
static get X() {
return [
[0, 1],
[1, 0]
];
}
Pauli-Y Gate
static get Y() {
return [
[0, negI],
[i, 0]
];
}
Pauli-Z Gate
static get Z() {
return [
[1, 0],
[0, -1]
];
}
Hadamard Gate
static get H() {
return math.multiply(1 / math.sqrt(2), [
[1, 1],
[1, -1]
]);
}
Identity Gate
static get Id() {
return math.eye(2);
}
S & S Dagger Gate
static get S() {
return [
[1, 0],
[0, i]
];
}
static get SDagger() {
return math.conj(
math.transpose(
[
[1, 0],
[0, i]
]
)
);
}
T & T Dagger / Pi over 8 Gate
static get T() {
return [
[1, 0],
[0, math.pow(math.e, (math.multiply(i, math.pi / 4)))]
];
}
static get TDagger() {
return math.conj(
math.transpose(
[
[1, 0],
[0, math.pow(math.e, (math.multiply(i, math.pi / 4)))]
]
)
);
}
/* #################################################
# Helper Functions #
################################################# */
static generateGate(gate, numQubits, qubit1, qubit2 = 1) {
if (math.typeof(gate) === 'string') {
if (gate === 'CNOT') {
Set the control and target qubits
const control = qubit1;
const target = qubit2;
Grab the gates now for easy access in the function
const identity = math.eye(2, 'sparse');
const X = math.matrix(this.X, 'sparse');
This matrix is the ‘Control Matrix’. At the end of the gate generation, the NaN’s positions will be evaluated to figure out if it should be a 0 or a 1
const C = math.matrix([
[NaN, 0],
[0, 1]
], 'sparse');
Turn the gate order into an array, so that it can be reduced later.
let gateOrder = [];
for (let i = 1; i <= numQubits; i++) {
if (i === control) {
gateOrder.push(C);
} else if (i === target) {
gateOrder.push(X);
} else {
gateOrder.push(identity);
}
}
Now the gateOrder array is taken and reduced using the ‘Kronecker Product’
let newGate = math.matrix(
gateOrder.reduce((a, b) => math.kron(a, b)),
'sparse'
);
Loop through the new matrix and if the NaN’s are in the center, then replace with a 0, otherwise, replace with a 1
newGate = math.map(newGate, (val, index) => {
if (math.isNaN(val)) {
if (index[0] === index[1]) {
return 1;
} else {
return 0;
}
} else {
return val;
}
});
Return the expanded gate.
return newGate;
} else {
Put the gates here for easy access
const identity = math.eye(2, 'sparse');
const mainGate = math.matrix(this[gate], 'sparse');
Again, Turn the gate order into an array, so that it can be reduced later.
let gateOrder = [];
for (let i = 1; i <= numQubits; i++) {
if (i === qubit1) {
gateOrder.push(mainGate);
} else {
gateOrder.push(identity);
}
}
Reduce and return the expanded gate.
return gateOrder.reduce((a, b) => math.kron(a, b));
}
} else if (math.typeof(gate) === 'Matrix'
|| math.typeof(gate) === 'Array') {
This expand / generates a gate from a user inputed matrix
Put the gates here for easy access
const identity = math.eye(2, 'sparse');
const mainGate = math.matrix(gate, 'sparse');
Gate must be unitary so check here
if (!helper.isUnitary(mainGate)) {
It’s not unitary, throw an Error
throw new Error('The gate supplied to generateGate is not unitary');
}
Again, Turn the gate order into an array, so that it can be reduced later.
let gateOrder = [];
for (let i = 1; i <= numQubits; i++) {
if (i === qubit1) {
gateOrder.push(mainGate);
} else {
gateOrder.push(identity);
}
}
Reduce and return the expanded gate.
return gateOrder.reduce((a, b) => math.kron(a, b));
}
Doesn’t match a default case, return an Id gate
return math.eye(math.pow(2, numQubits), 'sparse');
}
}
module.exports = gates;