At Section "Quantum computing is just matrix multiplication" we saw that making a quantum circuit actually comes down to designing one big unitary matrix.
We have to say though that that was a bit of a lie.
Quantum programmers normally don't just produce those big matrices manually from scratch.
Instead, they use quantum logic gates.
One key insight, is that the matrix of a non-trivial quantum circuit is going to be huge, and won't fit into any amount classical memory that can be present in this universe.
This is because the matrix is exponential in the number qubits, and is more than the number of atoms in the universe!
Therefore, off the bat we know that we cannot possibly describe those matrices in an explicit form, but rather must use some kind of shorthand.
But it gets worse.
Even if we had enough memory, the act of explicitly computing the matrix is not generally possible.
This is because knowing the matrix, basically means knowing the probability result for all possible outputs for each of the possible inputs.
But if we had those probabilities, our algorithmic problem would already be solved in the first place! We would "just" go over each of those output probabilities (OK, there are of those, which is also an insurmountable problem in itself), and the largest probability would be the answer.
So if we could calculate those probabilities on a classical machine, we would also be able to simulate the quantum computer on the classical machine, and quantum computing would not be able to give exponential speedups, which we know it does.
To see this, consider that for a given input, say 000 on a 3 qubit machine, the corresponding 8-sized quantum state looks like:
000 -> 1000 0000 == (1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
and therefore when you multiply it by the unitary matrix of the quantum circuit, what you get is the first column of the unitary matrix of the quantum circuit. And 001, gives the second column and so on.
As a result, to prove that a quantum algorithm is correct, we need to be a bit smarter than "just calculate the full matrix".
Which is why you should now go and read: Section "Quantum algorithm".
This type of thinking links back to how physical experiments relate to quantum computing: a quantum computer realizes a physical experiment to which we cannot calculate the probabilities of outcomes without exponential time.
So for example in the case of a photonic quantum computer, you are not able to calculate from theory the probability that photons will show up on certain wires or not.
One direct practical reason is that we need to map the matrix to real quantum hardware somehow, and all quantum hardware designs so far and likely in the future are gate-based: you manipulate a small number of qubits at a time (2) and add more and more of such operations.
While there are "quantum compilers" to increase the portability of quantum programs, it is to be expected that programs manually crafted for a specific hardware will be more efficient just like in classic computers.
TODO: is there any clear reason why computers can't beat humans in approximating any unitary matrix with a gate set?
This is analogous to what classic circuit programmers will do, by using smaller logic gates to create complex circuits, rather than directly creating one huge truth table.
The most commonly considered quantum gates take 1, 2, or 3 qubits as input.
The gates themselves are just unitary matrices that operate on the input qubits and produce the same number of output qubits.
For example, the matrix for the CNOT gate, which takes 2 qubits as input is:
1 0 0 0
0 1 0 0
0 0 0 1
0 0 1 0
The final question is then: if I have a 2 qubit gate but an input with more qubits, say 3 qubits, then what does the 2 qubit gate (4x4 matrix) do for the final big 3 qubit matrix (8x8)? In order words, how do we scale quantum gates up to match the total number of qubits?
The intuitive answer is simple: we "just" extend the small matrix with a larger identity matrix so that the sum of the probabilities third bit is unaffected.
More precisely, we likely have to extend the matrix in a way such that the partial measurement of the original small gate qubits leaves all other qubits unaffected.
For example, if the circuit were made up of a CNOT gate operating on the first and second qubits as in:
0 ----+----- 0
      |
1 ---CNOT--- 1

2 ---------- 2
then we would just extend the 2x2 CNOT gate to:
TODO lazy to properly learn right now. Apparently you have to use the Kronecker product by the identity matrix. Also, zX-calculus appears to provide a powerful alternative method in some/all cases.
Just like as for classic gates, we would like to be able to select quantum computer physical implementations that can represent one or a few gates that can be used to create any quantum circuit.
Unfortunately, in the case of quantum circuits this is obviously impossible, since the space of N x N unitary matrices is infinite and continuous.
Therefore, when we say that certain gates form a "set of universal quantum gates", we actually mean that "any unitary matrix can be approximated to arbitrary precision with enough of these gates".
Or if you like fancy Mathy words, you can say that the subgroup of the unitary group generated by our basic gate set is a dense subset of the unitary group.
The first two that you should study are:
The Hadamard gate takes or (quantum states with probability 1.0 of measuring either 0 or 1), and produces states that have equal probability of 0 or 1.
Figure 1. Hadamard gate symbol. Source.
The quantum NOT gate swaps the state of and , i.e. it maps:
As a result, this gate also inverts the probability of measuring 0 or 1, e.g.
  • if the old probability of 0 was 0, then it becomes 1
  • if the old probability of 0 was 0.2, then it becomes 0.8
Figure 1. Quantum NOT gate symbol. Source.
The most common way to construct multi-qubit gates is to use single-qubit gates as part of a controlled quantum gate.
Controlled quantum gates are gates that have two types of input qubits:
These gates can be understood as doing a certain unitary operation only if the control qubits are enabled or disabled.
The first example to look at is the CNOT gate.
Figure 1. Generic controlled quantum gate symbol. Source.
The black dot means "control qubit", and "U" means an arbitrary Unitary operation.
When the operand has a conventional symbol, e.g. the Figure "Quantum NOT gate symbol" for the quantum NOT gate to form the CNOT gate, that symbol is used in the operand instead.
Some authors use the convention of:
  • filled black circle: conventional controlled quantum gate, i.e. operate if control qubit is active
  • empty (White) circle: operarate if control qubit is inactive
The CNOT gate is a controlled quantum gate that operates on two qubits, flipping the second (operand) qubit if the first (control) qubit is set.
This gate is the first example of a controlled quantum gate that you should study.
Figure 1. CNOT gate symbol. Source. The symbol follow the generic symbol convention for controlled quantum gates shown at Figure "Generic controlled quantum gate symbol", but replacing the generic "U" with the Figure "Quantum NOT gate symbol".
To understand why the gate is called a CNOT gate, you should think as follows.
First let's produce a generic quantum state vector where the control qubit is certain to be 0.
On the standard basis:
we see that this means that only and should be possible. Therefore, the state must be of the form:
where and are two complex numbers such that
If we operate the CNOT gate on that state, we obtain:
and so the input is unchanged as desired, because the control qubit is 0.
If however we take only states where the control qubit is for sure 1:
Therefore, in that case, what happened is that the probabilities of and were swapped from and to and respectively, which is exactly what the quantum NOT gate does.
So from this we understand more concretelly what "the gate only operates if the first qubit is set to one" means.
Now go and study the Bell state and understand intuitively how this gate is used to produce it.
This gate set alone is not a set of universal quantum gates.
Notably, circuits containing those gates alone can be fully simulated by classical computers according to the Gottesman-Knill theorem, so there's no way they could be universal.
This means that if we add any number of Clifford gates to a quantum circuit, we haven't really increased the complexity of the algorithm, which can be useful as a transformational device.
A popular set of universal quantum gates derived from Clifford gates is Clifford plus T.
Set of quantum logic gate composed of the Clifford gates plus the Toffoli gate. It forms a set of universal quantum gates.