From d28b65df07cb0db099029c90365713e7865efe26 Mon Sep 17 00:00:00 2001
From: itsubaki <1759459+itsubaki@users.noreply.github.com>
Date: Mon, 13 Jan 2025 11:33:27 +0900
Subject: [PATCH 1/3] Add AddControlled API

---
 quantum/gate/gate.go      | 21 +++++++++++
 quantum/gate/gate_test.go | 73 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 94 insertions(+)

diff --git a/quantum/gate/gate.go b/quantum/gate/gate.go
index 0dfd15f..44b3252 100644
--- a/quantum/gate/gate.go
+++ b/quantum/gate/gate.go
@@ -133,7 +133,27 @@ func RZ(theta float64) matrix.Matrix {
 	}
 }
 
+// AddControlled returns a controlled-u gate with control bit.
+// u is a (2**n x 2**n) unitary matrix and returns a (2**n x 2**n) matrix.
+func AddControlled(u matrix.Matrix, n int, c int) matrix.Matrix {
+	s := 1 << n
+	g := I(n)
+
+	for i := 0; i < s; i++ {
+		if (i>>(n-1-c))&1 == 0 {
+			continue
+		}
+
+		for j := 0; j < s; j++ {
+			g[i][j] = u[i][j]
+		}
+	}
+
+	return g
+}
+
 // Controlled returns a controlled-u gate.
+// u is a (2x2) unitary matrix and returns a (2**n x 2**n) matrix.
 func Controlled(u matrix.Matrix, n int, c []int, t int) matrix.Matrix {
 	var mask int
 	for _, bit := range c {
@@ -360,6 +380,7 @@ func ControlledModExp2(n, a, j, N, c int, t []int) matrix.Matrix {
 	return g
 }
 
+// TensorProduct returns the tensor product of 'u' at specified indices over 'n' qubits.
 func TensorProduct(u matrix.Matrix, n int, index []int) matrix.Matrix {
 	idx := make(map[int]bool)
 	for _, i := range index {
diff --git a/quantum/gate/gate_test.go b/quantum/gate/gate_test.go
index 51210f5..bbf4c17 100644
--- a/quantum/gate/gate_test.go
+++ b/quantum/gate/gate_test.go
@@ -3,6 +3,7 @@ package gate_test
 import (
 	"fmt"
 	"math"
+	"math/rand/v2"
 	"strconv"
 	"testing"
 
@@ -167,6 +168,78 @@ func ExampleTensorProduct() {
 	// [(0+0i) (1+0i) (0+0i) (0+0i)]
 }
 
+func TestAddControlled(t *testing.T) {
+	u := gate.U(rand.Float64(), rand.Float64(), rand.Float64())
+
+	cases := []struct {
+		in     matrix.Matrix
+		want   matrix.Matrix
+		n, bit int
+	}{
+		{
+			in:   gate.TensorProduct(gate.X(), 2, []int{1}),
+			want: gate.ControlledNot(2, []int{0}, 1),
+			n:    2,
+			bit:  0,
+		},
+		{
+			in:   gate.ControlledNot(3, []int{0}, 2),
+			want: gate.ControlledNot(3, []int{0, 1}, 2),
+			n:    3,
+			bit:  1,
+		},
+		{
+			in:   gate.ControlledNot(3, []int{0}, 2),
+			want: gate.ControlledNot(3, []int{0}, 2),
+			n:    3,
+			bit:  0,
+		},
+		{
+			in:   gate.Controlled(u, 3, []int{0}, 1),
+			want: gate.Controlled(u, 3, []int{0, 2}, 1),
+			n:    3,
+			bit:  2,
+		},
+		{
+			in:   gate.Controlled(u, 3, []int{0}, 2),
+			want: gate.Controlled(u, 3, []int{0, 1}, 2),
+			n:    3,
+			bit:  1,
+		},
+		{
+			in:   gate.Controlled(u, 3, []int{1}, 2),
+			want: gate.Controlled(u, 3, []int{0, 1}, 2),
+			n:    3,
+			bit:  0,
+		},
+		{
+			in:   gate.Controlled(u, 3, []int{1}, 0),
+			want: gate.Controlled(u, 3, []int{1, 2}, 0),
+			n:    3,
+			bit:  2,
+		},
+		{
+			in:   gate.Controlled(u, 3, []int{2}, 0),
+			want: gate.Controlled(u, 3, []int{2, 1}, 0),
+			n:    3,
+			bit:  1,
+		},
+		{
+			in:   gate.Controlled(u, 3, []int{2}, 1),
+			want: gate.Controlled(u, 3, []int{0, 2}, 1),
+			n:    3,
+			bit:  0,
+		},
+	}
+
+	for _, c := range cases {
+		got := gate.AddControlled(c.in, c.n, c.bit)
+		if !got.Equals(c.want) {
+			t.Fail()
+		}
+	}
+}
+
 func TestControlledModExp2(t *testing.T) {
 	g1 := matrix.Apply(
 		gate.CNOT(7, 3, 5),

From 45e0a56bbabaaafc0de9316723d8a4cd888a7f45 Mon Sep 17 00:00:00 2001
From: itsubaki <1759459+itsubaki@users.noreply.github.com>
Date: Mon, 13 Jan 2025 11:35:59 +0900
Subject: [PATCH 2/3] Update AddControlled

---
 quantum/gate/gate.go      |  9 +++++----
 quantum/gate/gate_test.go | 17 ++++-------------
 2 files changed, 9 insertions(+), 17 deletions(-)

diff --git a/quantum/gate/gate.go b/quantum/gate/gate.go
index 44b3252..85d3bd1 100644
--- a/quantum/gate/gate.go
+++ b/quantum/gate/gate.go
@@ -135,16 +135,17 @@ func RZ(theta float64) matrix.Matrix {
 
 // AddControlled returns a controlled-u gate with control bit.
 // u is a (2**n x 2**n) unitary matrix and returns a (2**n x 2**n) matrix.
-func AddControlled(u matrix.Matrix, n int, c int) matrix.Matrix {
-	s := 1 << n
+func AddControlled(u matrix.Matrix, c int) matrix.Matrix {
+	d, _ := u.Dimension()
+	n := number.Log2(d)
 	g := I(n)
 
-	for i := 0; i < s; i++ {
+	for i := 0; i < d; i++ {
 		if (i>>(n-1-c))&1 == 0 {
 			continue
 		}
 
-		for j := 0; j < s; j++ {
+		for j := 0; j < d; j++ {
 			g[i][j] = u[i][j]
 		}
 	}
diff --git a/quantum/gate/gate_test.go b/quantum/gate/gate_test.go
index bbf4c17..bae715c 100644
--- a/quantum/gate/gate_test.go
+++ b/quantum/gate/gate_test.go
@@ -172,68 +172,59 @@ func TestAddControlled(t *testing.T) {
 	u := gate.U(rand.Float64(), rand.Float64(), rand.Float64())
 
 	cases := []struct {
-		in     matrix.Matrix
-		want   matrix.Matrix
-		n, bit int
+		in   matrix.Matrix
+		want matrix.Matrix
+		bit  int
 	}{
 		{
 			in:   gate.TensorProduct(gate.X(), 2, []int{1}),
 			want: gate.ControlledNot(2, []int{0}, 1),
-			n:    2,
 			bit:  0,
 		},
 		{
 			in:   gate.ControlledNot(3, []int{0}, 2),
 			want: gate.ControlledNot(3, []int{0, 1}, 2),
-			n:    3,
 			bit:  1,
 		},
 		{
 			in:   gate.ControlledNot(3, []int{0}, 2),
 			want: gate.ControlledNot(3, []int{0}, 2),
-			n:    3,
 			bit:  0,
 		},
 		{
 			in:   gate.Controlled(u, 3, []int{0}, 1),
 			want: gate.Controlled(u, 3, []int{0, 2}, 1),
-			n:    3,
 			bit:  2,
 		},
 		{
 			in:   gate.Controlled(u, 3, []int{0}, 2),
 			want: gate.Controlled(u, 3, []int{0, 1}, 2),
-			n:    3,
 			bit:  1,
 		},
 		{
 			in:   gate.Controlled(u, 3, []int{1}, 2),
 			want: gate.Controlled(u, 3, []int{0, 1}, 2),
-			n:    3,
 			bit:  0,
 		},
 		{
 			in:   gate.Controlled(u, 3, []int{1}, 0),
 			want: gate.Controlled(u, 3, []int{1, 2}, 0),
-			n:    3,
 			bit:  2,
 		},
 		{
 			in:   gate.Controlled(u, 3, []int{2}, 0),
 			want: gate.Controlled(u, 3, []int{2, 1}, 0),
-			n:    3,
 			bit:  1,
 		},
 		{
 			in:   gate.Controlled(u, 3, []int{2}, 1),
 			want: gate.Controlled(u, 3, []int{0, 2}, 1),
-			n:    3,
 			bit:  0,
 		},
 	}
 
 	for _, c := range cases {
-		got := gate.AddControlled(c.in, c.n, c.bit)
+		got := gate.AddControlled(c.in, c.bit)
 		if !got.Equals(c.want) {
 			t.Fail()
 		}

From d8458c204d1e350e3d23bfb42be235a925e08334 Mon Sep 17 00:00:00 2001
From: itsubaki <1759459+itsubaki@users.noreply.github.com>
Date: Mon, 13 Jan 2025 11:45:53 +0900
Subject: [PATCH 3/3] Update test

---
 quantum/gate/gate_test.go | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/quantum/gate/gate_test.go b/quantum/gate/gate_test.go
index bae715c..9e42cde 100644
--- a/quantum/gate/gate_test.go
+++ b/quantum/gate/gate_test.go
@@ -181,6 +181,11 @@ func TestAddControlled(t *testing.T) {
 			want: gate.ControlledNot(2, []int{0}, 1),
 			bit:  0,
 		},
+		{
+			in:   gate.TensorProduct(gate.X(), 2, []int{0}),
+			want: gate.ControlledNot(2, []int{1}, 0),
+			bit:  1,
+		},
 		{
 			in:   gate.ControlledNot(3, []int{0}, 2),
 			want: gate.ControlledNot(3, []int{0, 1}, 2),