# Systems of Qubits and Entanglement

```elixir
Mix.install([
  {:qx, "~> 0.8.0", hex: :qx_sim},
  {:kino, "~> 0.12"},
  {:vega_lite, "~> 0.1.11"},
  {:kino_vega_lite, "~> 0.1.11"}
])

alias Qx.Register
```

## Learning objectives

By the end of this tutorial you will be able to:

* Represent a two-qubit system as a four-dimensional state vector, and build it as the tensor product of single-qubit states
* Tell a product state from an entangled one, and explain why the difference shows up only when you measure
* Construct all four Bell states from $H$ and $CNOT$ and read their amplitudes with `Qx.Register.show_state/1`
* Measure a multi-qubit register with `Qx.measure_all/1`, read the joint-outcome counts, and explain the correlations you see
* Build a three-qubit GHZ state and predict its measurement statistics before running it

## Introduction

This is the fourth tutorial in the series, following **Quantum Measurement**. So far every state has lived on a single qubit. Real computations need many qubits working together, and the moment you have two, something appears that has no classical counterpart: **entanglement**, qubits whose measurement outcomes are locked together even though neither one holds a definite value on its own.

Two threads run through this tutorial. The first is how qubits combine: the **tensor product**, which takes two 2-dimensional state spaces and builds a 4-dimensional one. The second is what happens when you **measure** the combined system. Measurement is where entanglement stops being an abstract property of a state vector and starts producing correlations you can count. That payoff, reading joint outcomes off a chart, is the heart of the tutorial.

We work mostly in **calculation mode** (`Qx.Register`) when we want to see amplitudes, and switch to **circuit mode** (`Qx.create_circuit` and the top-level `Qx.*` functions) whenever we want to measure with shots.

**Prerequisites:** the first three tutorials in this series. You should be comfortable with single-qubit states and the $X$, $H$, $Z$ gates, the Born rule and collapse, and reading a `Qx.draw_counts/2` chart.

## Two qubits and the tensor product

A single qubit lives in a 2-dimensional space spanned by $\ket{0}$ and $\ket{1}$. Put two qubits side by side and the combined system has $2 \times 2 = 4$ dimensions, spanned by the four ways two classical bits can be set. The tool that builds the joint space from the parts is the **tensor product**, written $\otimes$.

The four basis states are

$$
\ket{00}, \quad \ket{01}, \quad \ket{10}, \quad \ket{11}
$$

and a general two-qubit state is a superposition of all four:

$$
\ket{\psi} = c_{00}\ket{00} + c_{01}\ket{01} + c_{10}\ket{10} + c_{11}\ket{11}, \qquad \sum |c|^2 = 1
$$

A register starts with every qubit in $\ket{0}$, so a fresh 2-qubit register is $\ket{00}$:

```elixir
reg = Register.new(2)
Register.show_state(reg)
```

One amplitude of 1 on $\ket{00}$, zero everywhere else. Now put each qubit into superposition with its own Hadamard. Qubit 0 becomes $\ket{+}$, qubit 1 becomes $\ket{+}$, and the joint state is their tensor product $\ket{+} \otimes \ket{+}$:

```elixir
reg = Register.new(2) |> Register.h(0) |> Register.h(1)
Register.show_state(reg)
```

Four equal amplitudes of $0.5$, because $\tfrac{1}{\sqrt{2}} \times \tfrac{1}{\sqrt{2}} = \tfrac{1}{2}$. This is a **product state**: each qubit is doing its own thing, and the joint state is just the two single-qubit states multiplied together.

A note on labels before we go further. Qx writes basis states with **qubit 0 on the left**, so $\ket{10}$ means qubit 0 is $\ket{1}$ and qubit 1 is $\ket{0}$. Confirm it for yourself by flipping only qubit 0 and measuring:

```elixir
# Set qubit 0 to |1⟩, leave qubit 1 at |0⟩, then measure both
Qx.create_circuit(2, 2)
|> Qx.x(0)
|> Qx.measure_all()
|> Qx.run(shots: 100)
|> Qx.draw_counts(title: "x(0) only — every shot reads 10")
```

Every shot lands on `10`: qubit 0 flipped to 1, qubit 1 stayed at 0. That left-to-right convention is how we'll read every counts chart from here on.

## Entanglement: the Bell state

The product state above factors cleanly into "qubit 0's state" times "qubit 1's state." The question that opens up quantum computing is whether *every* two-qubit state factors that way. It does not. The states that refuse are **entangled**, and the simplest one is the $\Phi^+$ **Bell state**:

$$
\ket{\Phi^+} = \tfrac{1}{\sqrt{2}}\bigl(\ket{00} + \ket{11}\bigr)
$$

There is no way to write this as $\ket{a} \otimes \ket{b}$ for single-qubit states $\ket{a}$ and $\ket{b}$. Try, and you find the cross terms $\ket{01}$ and $\ket{10}$ always leak in. The recipe to build it is a Hadamard on qubit 0, then a CNOT with qubit 0 controlling qubit 1:

```elixir
bell = Register.new(2) |> Register.h(0) |> Register.cx(0, 1)
Register.show_state(bell)
```

Two amplitudes of about $0.707$ on $\ket{00}$ and $\ket{11}$, and exactly zero on $\ket{01}$ and $\ket{10}$. The Hadamard spreads qubit 0 into $\tfrac{1}{\sqrt{2}}(\ket{0} + \ket{1})$, and the CNOT ties qubit 1 to it: where qubit 0 is 0, qubit 1 stays 0; where qubit 0 is 1, qubit 1 flips to 1. The two qubits are now welded together.

Building the state by hand is the point while you're learning the recipe. Once you know it, Qx gives you all four Bell states as a one-liner, `Qx.bell_state/1`, which returns the prepared circuit. It's the right choice when a Bell state is a building block in some larger protocol rather than the thing you're teaching:

```elixir
# The same Φ⁺ state, prepared in one call
Qx.bell_state(:phi_plus) |> Qx.get_state()
```

The state vector reads $0.707$ on the first and last components and zero between, identical to the circuit we built from $H$ and $CNOT$. The four atoms are `:phi_plus` (the default), `:phi_minus`, `:psi_plus`, and `:psi_minus`.

## Measuring a multi-qubit state

This is where entanglement becomes something you can count. Measure both qubits of $\Phi^+$ many times and watch which outcomes appear:

```elixir
phi_plus =
  Qx.create_circuit(2, 2)
  |> Qx.h(0)
  |> Qx.cx(0, 1)
  |> Qx.measure_all()

Qx.draw_counts(Qx.run(phi_plus, shots: 1024), title: "Φ⁺ — joint measurement of both qubits")
```

Only `00` and `11`, each about half the shots. The cross outcomes `01` and `10` never appear. That is the correlation: the two bits always agree. You cannot predict whether a given shot will be `00` or `11`, but the instant you learn qubit 0's value you know qubit 1's with certainty. Measuring one qubit collapses the whole state, and the partner falls into line.

Compare that with the product state from earlier, where each qubit carries its own independent Hadamard:

```elixir
product =
  Qx.create_circuit(2, 2)
  |> Qx.h(0)
  |> Qx.h(1)
  |> Qx.measure_all()

Kino.Layout.grid(
  [
    Qx.draw_counts(Qx.run(phi_plus, shots: 1024), title: "Entangled Φ⁺ — only 00 and 11"),
    Qx.draw_counts(Qx.run(product, shots: 1024), title: "Product |+⟩⊗|+⟩ — all four")
  ],
  columns: 2
)
```

Both charts use the same gates, $H$ and one extra operation, yet they could not be more different. The product state spreads its shots across all four outcomes, because each qubit's result is an independent coin flip. The entangled state allows only the two matched outcomes. **Independence on the left, correlation on the right** is the whole story of entanglement, and measurement is the only place you can see it.

## The other three Bell states

$\Phi^+$ is one of four Bell states, a complete basis of maximally entangled two-qubit states. The other three come from adding a single gate before you measure. Adding an $X$ to qubit 1 turns the matched outcomes into mismatched ones, producing $\Psi^+$:

$$
\ket{\Psi^+} = \tfrac{1}{\sqrt{2}}\bigl(\ket{01} + \ket{10}\bigr)
$$

```elixir
psi_plus =
  Qx.create_circuit(2, 2)
  |> Qx.h(0)
  |> Qx.cx(0, 1)
  |> Qx.x(1)
  |> Qx.measure_all()

Qx.draw_counts(Qx.run(psi_plus, shots: 1024), title: "Ψ⁺ — the bits always disagree")
```

Now only `01` and `10` appear: the qubits are still perfectly correlated, but the correlation is anti-matched. Whatever qubit 0 reads, qubit 1 reads the opposite.

The remaining two, $\Phi^-$ and $\Psi^-$, differ from $\Phi^+$ and $\Psi^+$ only by a minus sign in the amplitudes (add a $Z$ on qubit 0 to flip it). And here is a callback to the measurement tutorial: that minus sign is **relative phase**, and a Z-basis measurement is blind to it. $\Phi^+$ and $\Phi^-$ produce identical counts, `00` and `11` at 50/50, even though they are different states. Telling them apart needs a measurement in a different basis, exactly as $\ket{+}$ and $\ket{-}$ did with a single qubit.

```elixir
phi_minus =
  Qx.create_circuit(2, 2)
  |> Qx.h(0)
  |> Qx.cx(0, 1)
  |> Qx.z(0)
  |> Qx.measure_all()

Kino.Layout.grid(
  [
    Qx.draw_counts(Qx.run(phi_plus, shots: 1024), title: "Φ⁺"),
    Qx.draw_counts(Qx.run(phi_minus, shots: 1024), title: "Φ⁻ — same counts, different state")
  ],
  columns: 2
)
```

Two distinct states, one identical readout. The phase that separates them survives right up until the Z-basis measurement throws it away.

## Scaling up: the GHZ state

Entanglement is not limited to pairs. The three-qubit **GHZ state** extends the $\Phi^+$ pattern to a register where all three qubits agree:

$$
\ket{\text{GHZ}} = \tfrac{1}{\sqrt{2}}\bigl(\ket{000} + \ket{111}\bigr)
$$

The recipe generalises the Bell recipe: a Hadamard on qubit 0, then a cascade of CNOTs carrying its value down the line.

**Predict first:** with only $\ket{000}$ and $\ket{111}$ in the superposition, which outcomes can a measurement produce, and in what proportion? Then run it:

```elixir
ghz =
  Qx.create_circuit(3, 3)
  |> Qx.h(0)
  |> Qx.cx(0, 1)
  |> Qx.cx(1, 2)
  |> Qx.measure_all()

Qx.draw_counts(Qx.run(ghz, shots: 1024), title: "GHZ — three qubits, all or nothing")
```

Only `000` and `111`, each about half the shots, with all six other outcomes empty. Measure any one of the three qubits and you have determined the other two.

Two shortcuts are worth knowing once the recipe is familiar. `Qx.cx_chain/2` collapses the CNOT cascade into a single call, `Qx.cx_chain([0, 1, 2])`, which matters as the register grows. And `Qx.ghz_state/1` builds the whole preparation circuit for you, defaulting to three qubits and taking a count for larger systems:

```elixir
# A 4-qubit GHZ preparation circuit, built in one call
Qx.ghz_state(4) |> Qx.get_state()
```

The state vector has $0.707$ on the first and last of its 16 components, $\ket{0000}$ and $\ket{1111}$, and zero everywhere else: the GHZ pattern at any size.

## Summary

* **The tensor product** combines qubits: two qubits span a 4-dimensional space with basis $\ket{00}, \ket{01}, \ket{10}, \ket{11}$, and $n$ qubits span $2^n$ dimensions. Qx labels these states with qubit 0 on the left.
* **A product state factors** into independent single-qubit states; an **entangled state** does not, and $\Phi^+ = \tfrac{1}{\sqrt{2}}(\ket{00} + \ket{11})$ is the simplest example.
* **Measurement reveals entanglement.** A product state spreads across all outcomes; an entangled state allows only correlated ones. Measuring one qubit fixes the rest.
* **The four Bell states** form a complete basis of maximally entangled pairs, distinguished by whether the bits match or disagree and by a relative phase that a Z-basis measurement cannot see.
* **GHZ** carries the pattern to three or more qubits, where a single measurement determines the entire register.
* **Built-in shortcuts** prepare these states once you know the recipe: `Qx.bell_state/1` for the four Bell states and `Qx.ghz_state/1` for an $n$-qubit GHZ state, with `Qx.cx_chain/2` for the CNOT cascade.

### What's next

In the next tutorial, **Interference and Phase Kickback**, we meet the mechanism that turns superposition and phase into a computational advantage. You will watch amplitudes cancel and reinforce, and see how a controlled gate stamps a phase onto a register, the two ideas that drive every algorithm in the series. From there, **Bernstein–Vazirani and Grover's Search** puts them to work.
