```python
from IPython.core.display import HTML, Image
css_file = 'style.css'
HTML(open(css_file, 'r').read())
```

<link href='http://fonts.googleapis.com/css?family=Alegreya+Sans:100,300,400,500,700,800,900,100italic,300italic,400italic,500italic,700italic,800italic,900italic' rel='stylesheet' type='text/css'>
<link href='http://fonts.googleapis.com/css?family=Arvo:400,700,400italic' rel='stylesheet' type='text/css'>
<link href='http://fonts.googleapis.com/css?family=PT+Mono' rel='stylesheet' type='text/css'>
<link href='http://fonts.googleapis.com/css?family=Shadows+Into+Light' rel='stylesheet' type='text/css'>
<link href='http://fonts.googleapis.com/css?family=Philosopher:400,700,400italic,700italic' rel='stylesheet' type='text/css'>

```python
from sympy import init_printing, Matrix, symbols, eye
from warnings import filterwarnings
```

```python
init_printing(use_latex = 'mathjax')
filterwarnings('ignore')
```

# Similar matrices

## Positive definite matrices

* Remember the following from positive definite matrices
$$ {x}^{T}{A}{x}>{0}; {x}\ne{0} $$
* These always refer to symmetric matrices
* What do we know about their inverses?
    * We can't say anything about their pivots
    * The following is true for their eigenvalues, though
    $$ {\lambda}_{{A}^{-1}}=\frac{1}{{\lambda}_{A}} $$
    * The inverse is also positive definite
* If both A and B are positive definite
    * We don't know the pivots of (A+B)
    * We don't know the eigenvalues of (A+B)
    * We could look at the following (which is true)
    $$ {x}^{T}\left({A}+{B}\right){x}>0 $$

* From least squares the *m*×*n* matrix A (is not square, neither symmetric (for this section, though, we assume rank is *n*)) we used ATA, which is square and symmetric, but is it positive definite?
* Analogous with real numbers, where we ask is the square of any value positive?
    * Again we don't know is the pivots or eigenvalues
    * We do look at the following which is always positive (which you can show by grouping some terms)
    $$ {x}^{T}{A}^{T}{A}{x} \\ =\left({{A}{x}}\right)^{T}\left({A}{x}\right) \\ ={ \left\| Ax \right\|  }^{ 2 } $$
    * This last statement is just the length of A**x**, which must be positive (or zero; only if **x**=0)

## Similar matrices

* Consider two similar, square matrices A and B (no longer with the requirement that they are symmetric)
* They have similar sizes, though
* The similarity lies in the fact that there is some invertible matrix M for which the following holds
$$ {B}={M}^{-1}{A}{M} $$

* Remember the creation of the diagonal matrix using the eigenvector matrix
$$ {S}^{-1}{A}{S}={\Lambda} $$
* This says A is similar to Λ

* Now we consider some (invertible) matrix M and create a matrix B from M-1AM
* We state that B is then similar to A (it is now part of some family of matrices of A, the *neatest* of which is the diagonal matrix Λ for its creation via the eigenvector matrix of A)

```python
A = Matrix([[2, 1], [1, 2]])
A
```

$$\left[\begin{matrix}2 & 1\\1 & 2\end{matrix}\right]$$

```python
S, D = A.diagonalize() # S is the eigenvector matrix
```

```python
S.inv() * A * S # The matrix Lambda
```

$$\left[\begin{matrix}1 & 0\\0 & 3\end{matrix}\right]$$

* Now let's invent a matrix M

```python
M = Matrix([[1, 4], [0, 1]])
M
```

$$\left[\begin{matrix}1 & 4\\0 & 1\end{matrix}\right]$$

```python
B = M.inv() * A * M
A, B # Printing both to the screen
```

$$\begin{pmatrix}\left[\begin{matrix}2 & 1\\1 & 2\end{matrix}\right], & \left[\begin{matrix}-2 & -15\\1 & 6\end{matrix}\right]\end{pmatrix}$$

* What does A and B have in common?
* The have the same eigenvalues

```python
A.eigenvals(), B.eigenvals() # The solution is in the form {eigenvalue:how many times that that value occur...}
```

$$\begin{pmatrix}\begin{Bmatrix}1 : 1, & 3 : 1\end{Bmatrix}, & \begin{Bmatrix}1 : 1, & 3 : 1\end{Bmatrix}\end{pmatrix}$$

* All similar-sized matrices with the same eigenvalues are similar matrices
* The most *special* member of this family is the diagonal matrix with the eigenvalues on the main diagonal

$$ Ax=\lambda x\\ A{ M }^{ -1 }x=\lambda { M }^{ -1 }x\\ \because \quad { M }^{ -1 }M=I\\ { M }^{ -1 }AM{ M }^{ -1 }x=\lambda { M }^{ -1 }x\\ \because \quad B={ M }^{ -1 }AM\\ B{ M }^{ -1 }x=\lambda { M }^{ -1 }x $$

* The eigenvectors are not the same though

```python
A.eigenvects(), B.eigenvects()
```

$$\begin{pmatrix}\begin{bmatrix}\begin{pmatrix}1, & 1, & \begin{bmatrix}\left[\begin{matrix}-1\\1\end{matrix}\right]\end{bmatrix}\end{pmatrix}, & \begin{pmatrix}3, & 1, & \begin{bmatrix}\left[\begin{matrix}1\\1\end{matrix}\right]\end{bmatrix}\end{pmatrix}\end{bmatrix}, & \begin{bmatrix}\begin{pmatrix}1, & 1, & \begin{bmatrix}\left[\begin{matrix}-5\\1\end{matrix}\right]\end{bmatrix}\end{pmatrix}, & \begin{pmatrix}3, & 1, & \begin{bmatrix}\left[\begin{matrix}-3\\1\end{matrix}\right]\end{bmatrix}\end{pmatrix}\end{bmatrix}\end{pmatrix}$$

* Remember that we have a problem when eigenvalues are repeated for a matrix
* If this is so, we might not have a *full set* of eigenvectors and we cannot diagonalize

```python
A1 = Matrix([[4, 0], [0, 4]])
A2 = Matrix([[4, 1], [0, 4]])
```

```python
A1.eigenvals()
```

$$\begin{Bmatrix}4 : 2\end{Bmatrix}$$

```python
A2.eigenvals()
```

$$\begin{Bmatrix}4 : 2\end{Bmatrix}$$

* Both the two matrices A11 and A2 have two similar eigenvalues each, namely 4
* They are not similar, though
* There is no matrix M to use with A1 to produce A2

* Note that A1 is 4 multiplied by the identity matrix of size 2
* It is a small family, with only this member
* A2 is the *neatest* member of its much larger family
* Diagonalizing it is not possible, though, as if it was, it would results in A2 which is not in the same family, leaving A1 as the *neatest* family member

* The *nicest* (most diagonal one) is called the **Jordan form** of the family

* Let's find more members of A1

* The matrix A1 is
$$ \begin{bmatrix} 4 & 1 \\ 0 & 4 \end{bmatrix} $$
* The trace is 8, so let's choose 5 and 3
$$ \begin{bmatrix} 5 &  \\  & 3 \end{bmatrix} $$
* The determinant must remain 16, so let's choose 1 and -1
$$ \begin{bmatrix} 5 & 1 \\ -1 & 3 \end{bmatrix} $$

```python
A3 = Matrix([[5, 1], [-1, 3]])
A1.eigenvals() == A3.eigenvals() # Check to see if the eigenvalues are similar
```

    True

* So we have to add, similar independent columns of eigenvectors to the definition of similar matrices
* It's more than that, though

```python
A4 = Matrix([[0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 0], [0, 0, 0, 0]])
A4
```

$$\left[\begin{matrix}0 & 1 & 0 & 0\\0 & 0 & 1 & 0\\0 & 0 & 0 & 0\\0 & 0 & 0 & 0\end{matrix}\right]$$

```python
A4.eigenvals() # Four zeros
```

$$\begin{Bmatrix}0 : 4\end{Bmatrix}$$

```python
A4.eigenvects() # Rank of 2
```

$$\begin{bmatrix}\begin{pmatrix}0, & 4, & \begin{bmatrix}\left[\begin{matrix}1\\0\\0\\0\end{matrix}\right], & \left[\begin{matrix}0\\0\\0\\1\end{matrix}\right]\end{bmatrix}\end{pmatrix}\end{bmatrix}$$

```python
A5 = Matrix([[0, 1, 7, 0], [0, 0, 1, 0], [0, 0, 0, 0], [0, 0, 0, 0]])
A5
```

$$\left[\begin{matrix}0 & 1 & 7 & 0\\0 & 0 & 1 & 0\\0 & 0 & 0 & 0\\0 & 0 & 0 & 0\end{matrix}\right]$$

```python
A5.eigenvals()
```

$$\begin{Bmatrix}0 : 4\end{Bmatrix}$$

```python
A5.eigenvects()
```

$$\begin{bmatrix}\begin{pmatrix}0, & 4, & \begin{bmatrix}\left[\begin{matrix}1\\0\\0\\0\end{matrix}\right], & \left[\begin{matrix}0\\0\\0\\1\end{matrix}\right]\end{bmatrix}\end{pmatrix}\end{bmatrix}$$

```python
A6 = Matrix([[0, 1, 0, 0], [0, 0, 0, 0], [0, 0, 0, 1], [0, 0, 0, 0]])
A6
```

$$\left[\begin{matrix}0 & 1 & 0 & 0\\0 & 0 & 0 & 0\\0 & 0 & 0 & 1\\0 & 0 & 0 & 0\end{matrix}\right]$$

```python
A6.eigenvals()
```

$$\begin{Bmatrix}0 : 4\end{Bmatrix}$$

```python
A6.eigenvects()
```

$$\begin{bmatrix}\begin{pmatrix}0, & 4, & \begin{bmatrix}\left[\begin{matrix}1\\0\\0\\0\end{matrix}\right], & \left[\begin{matrix}0\\0\\1\\0\end{matrix}\right]\end{bmatrix}\end{pmatrix}\end{bmatrix}$$

```python
A4.eigenvects() == A5.eigenvects()
```

    True

```python
A4.eigenvects() == A6.eigenvects()
```

    False

* Jordan's theorem
    * Every square matrix A is similar to a Jordan matrix J
    * There is one eigenvector per block
    * The eigenvalues sit along the main diagonal
    * The matrices are not similar if the blocks are not of similar size
    * See problem 3 below where Jordan blocks are formed (they must actually both be broken down further into true Jordan blocks which will show the blocks to be of unequal size, instead I keep them in non-Jordan form (not correct) and show different number of pivots and thereby different eigenvectors)

## Example problems

### Example problem 1

* If A and B are similar matrices, why are the following similar?
$$ 2{A}^{3}+{A}-3{I} \\ 2{B}^{3}+{B}-3{I} $$

#### Solution

* There is some matrix such that the following is true
$$ {M}{A}{M}^{-1}={B} $$
* From this follows
$$ {M}\left(2{A}^{3}+{A}-3{I}\right){M}^{-1} \\ =\quad 2\left( MA{ M }^{ -1 }MA{ M }^{ -1 }MA{ M }^{ -1 } \right) +MA{ M }^{ -1 }-3MI{ M }^{ -1 }\\ =\quad 2{ B }^{ 3 }+B-3I  $$

* I.e. if two matrices (A and B) are similar any polynomial involving them (replacing A with B) are similar

### Example problem 2

* Are the two 3×3 matrices A and B , with eigenvalues 1, 0, -1 similar?

#### Solution

* Yes, because the eigenvalues are distinct (and diagonalizable)

### Example problem 3

* Are these two matrices similar?
$$ {J}_{1}=\begin{bmatrix}-1&1&0\\0&-1&1\\0&0&-1\end{bmatrix} \\ {J}_{2}=\begin{bmatrix}-1&1&0\\0&-1&0\\0&0&-1\end{bmatrix} $$

#### Solution

* No

```python
J1 = Matrix([[-1, 1, 0], [0, -1, 1], [0, 0, -1]])
J2 = Matrix([[-1, 1, 0], [0, -1, 0], [0, 0, -1]])
J1, J2
```

$$\begin{pmatrix}\left[\begin{matrix}-1 & 1 & 0\\0 & -1 & 1\\0 & 0 & -1\end{matrix}\right], & \left[\begin{matrix}-1 & 1 & 0\\0 & -1 & 0\\0 & 0 & -1\end{matrix}\right]\end{pmatrix}$$

* Let's create Jordan block from these

```python
J1 + eye(3), J2 + eye(3)
```

$$\begin{pmatrix}\left[\begin{matrix}0 & 1 & 0\\0 & 0 & 1\\0 & 0 & 0\end{matrix}\right], & \left[\begin{matrix}0 & 1 & 0\\0 & 0 & 0\\0 & 0 & 0\end{matrix}\right]\end{pmatrix}$$

* Jordan blocks have zeros on the main diagonal and various forms of 1 just above the main diagonal
* Note the difference between the Jordan blocks of J1 and J2
* The first now contains two pivots and the second only 1; they will not have the same number of eigenvectors and cannot be similar

```python

```

Mathematics/Linear Algebra/0.0 MIT-18.06 - Jupyter/Lecture_28_Similar_matrices_Jordan_form.ipynb


# Image Style Transferの実装

--------------------------------
Gatys, Ecker, and Bethge(2016)の画像スタイル変換を実装する。

【参考文献】
L. A. Gatys, A. S. Ecker, and M. Bethge,
Image style transfer using convolutional neural networks,
In Proceedings of the IEEE Conference on Computer Vision and Pattern Recognition, pages, 2414-2423, 2016.

```python
import numpy as np
import pandas as pd
import tensorflow as tf
```

```python
CONTENT_FILE = '/home/ishiyama/image_style_transfer/image/input/test_input_01.JPG'
STYLE_FILE = '/home/ishiyama/image_style_transfer/image/style/test_style_01.jpg'
```

```python
class Image(np.ndarray):

    """画像を扱うためのnumpy.ndarray

    XXX: 実装が大変なので一旦保留
         画像の形状の情報を属性で取り出せるようにしたい
    """

    DATA_FORMAT_CHAR = {
        'BATCH': 'N',
        'HEIGHT': 'H',
        'WIDTH': 'W',
        'CHANNEL': 'C'
    }

    def __new__(subtype,
                shape,
                dtype=float,
                buffer=None,
                offset=0,
                strides=None,
                order=None,
                data_format='NHWC'):

        super(__class__, self).__new__(subtype, shape, dtype, buffer, offset, strides, order)

        self.data_format = data_format
        num_batch, num_height, num_width, num_channel = self._get_image_shape(data_format=data_format)
        self.num_batch = num_batch
        self.num_height = num_height
        self.num_width = num_width
        self.num_channel = num_channel

    def _get_image_shape(self, data_format):
        _image_shape = self.shape
        idx_batch = self._get_index_data_format(data_format=data_format, data_type='BATCH'),
        idx_height = self._get_index_data_format(data_format=data_format, data_type='HEIGHT')
        idx_width = self._get_index_data_format(data_format=data_format, data_type='WIDTH')
        idx_channel = self._get_index_data_format(data_format=data_format, data_type='CHANNEL')
        reordered_image_shape = (_image_shape[idx_batch],
                                 _image_shape[idx_height],
                                 _image_shape[idx_width],
                                 _image_shape[idx_channel])
        return reordered_image_shape

    def _get_index_data_format(self, data_format, data_type):
        idx = data_format.find(__class__.DATA_FORMAT_CHAR[data_type])
        if idx == -1:
            raise ValueError('data type "{}" is not available.'.format(data_type))

        return idx

    @classmethod
    def reshape(self, *args):
        self = __class__(super(__class__, self).reshape(args))

```

```python
def read_images_as_jpeg(file):

    """
    JPEG Image Reader

    This function reads the content and style images as JPEG format.
    These image data must be square for now, different height and
    width will be able to supplied for future.

    Args:
        file : str. A path of the image file.

    Returns:
        A tuple. Each Elements are Tensor object of the read images.
    """

    filename_queue = tf.train.string_input_producer([file])
    reader = tf.WholeFileReader()
    key, value = reader.read(filename_queue)
    image = tf.image.decode_jpeg(value)

    # Read image is a Tensor object which tf.nn.conv2d cannot handle,
    # so convert it to numpy.ndarray.
    sess = tf.Session()
    sess.run(tf.global_variables_initializer())
    tf.train.start_queue_runners(sess)
    # Returned array's shape will be (height, width, channel).
    image_array_hwc = sess.run(image)
    new_shape = [1]
    new_shape.extend(image_array_hwc.shape)

    # return image_array_chw
    return image_array_hwc.reshape(new_shape)
```

```python
content_image = read_images_as_jpeg(file=CONTENT_FILE)
style_image = read_images_as_jpeg(file=STYLE_FILE)
```

# VGGを実装する
------------------------
画像の特徴量を抽出するアルゴリズムにはSimonyan and Zisserman(2015)で提案されたCNN(VGG19)の畳込み層とプーリング層が使われている。
ここでは、「TensorFlowで学ぶディープラーニング入門」の多層CNNの実装を参考にVGG19を構築する。

【参考文献】
K. Simonyan and A. Zisserman, Very Deep Convolutional Networks For Large-Scale Image Recognition, arXiv: 1409.1556v6, 2015
中井悦司, TensorFlowで学ぶディープラーニング入門〜畳み込みニューラルネットワーク徹底解説, マイナビ出版, 2016

### 畳み込み層を実装する

```python
import tensorflow as tf
import numpy as np
from tensorflow.examples.tutorials.mnist import input_data

def calculate_convolutional_layer(x, filter_height, filter_width, output_channels):

    """
    Executeing a convolutional layer task.

    Args:
        x                     : An image data.
        filter_height   (int) : A height of each filters.
        filter_width    (int) : A width of each filters.
        output_channels (int) : A number of channels which must be outputed.

    Returns:
        An activation of an convolutional layer.
    """

    if ((not isinstance(filter_height, int))
        or (not isinstance(filter_width, int))
        or (not isinstance(output_channels, int))):
        raise TypeError('"filter_height" and "filter_width" and "output_channels" '
                        'must be integer.')

    # TODO: 入力画像の縦、横、チャンネル数を属性で取得できるようにする
    # 例） input_channels = x.num_channels
    input_channels = int(x.shape[-1])
    filter_value = 1 / float(filter_height * filter_width)
    epsilon = filter_value / 10.0
    W = tf.Variable(
        tf.random_uniform(
            shape=[filter_height,
                   filter_width,
                   input_channels,
                   output_channels],
            minval=filter_value - epsilon,
            maxval=filter_value + epsilon
        )
    )
    h = tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')
    b = tf.Variable(tf.constant(0.1, shape=[output_channels]))
    convoluted_image = tf.nn.relu(h + b)

    return convoluted_image
```

```python
x = tf.placeholder(tf.float32, [1, 477, 477, 3])
test_model = calculate_convolutional_layer(
    x=x,
    filter_height=3,
    filter_width=3,
    output_channels=1
)
sess = tf.InteractiveSession()
# tf.Session()だと、sess.runで返ってくる行列の要素がすべて0だった。
# TODO: Sessionメソッド と InteractiveSessionメソッドの違いを調べる
# sess = tf.Session()
sess.run(tf.global_variables_initializer())
test_result = sess.run(test_model, feed_dict={x: content_image})
```

```python
test_result.shape
```

    (1, 477, 477, 1)

```python
test_result
```

    array([[[[ 114.27301025],
             [ 173.50660706],
             [ 174.29614258],
             ...,
             [ 480.10452271],
             [ 481.24404907],
             [ 323.35064697]],

            [[ 166.4135437 ],
             [ 252.51049805],
             [ 254.13793945],
             ...,
             [ 717.27807617],
             [ 718.06811523],
             [ 481.68475342]],

            [[ 158.3868866 ],
             [ 241.47108459],
             [ 244.67269897],
             ...,
             [ 718.62445068],
             [ 718.66595459],
             [ 481.57723999]],

            ...,
            [[ 448.78652954],
             [ 674.51971436],
             [ 673.51464844],
             ...,
             [ 672.70135498],
             [ 670.52099609],
             [ 449.13308716]],

            [[ 449.70996094],
             [ 676.00695801],
             [ 674.76342773],
             ...,
             [ 673.22253418],
             [ 671.83905029],
             [ 450.01858521]],

            [[ 297.78442383],
             [ 444.92471313],
             [ 443.75894165],
             ...,
             [ 443.45913696],
             [ 442.98733521],
             [ 296.54943848]]]], dtype=float32)

### Maxプーリング層を実装する

```python
def calculate_max_pooling_layer(x, ksize, strides):

    """Wrapper function of tf.nn.max_pool.

    Args:
        x       : A Tensor produced by calculate_convolutional_layer.
        ksize   : A list of ints that has length >= 4. The size of
                  the window for each dimension of the input tensor.
        strides : A list of ints that has length >= 4. The stride
                  of the sliding window for each dimension of the
                  input tensor.

    Returns:
        A pooled image.
    """

    pooled_image = tf.nn.max_pool(x, ksize=ksize, strides=strides, padding='SAME')

    return pooled_image
```

### 畳込みとプーリング処理の途中経過を保持するクラスを実装する

```python
class FeatureMapHolder(object):

    def __init__(self):

        self.conv = []
        self.pool = None

    def set_conv(self, mat):

        self.conv.append(mat)

    def get_conv(self, idx):

        if idx is None:
            raise ValueError('idx is required.')

        if not isinstance(idx, int):
            raise ValueError('idx must be an integer.')

        return self.conv[idx]

    def set_pool(self, mat):

        self.pool = mat

    def get_pool(self):

        return self.pool

    def get(self, type, idx=None):

        if type == 'pool':
            return self.get_pool()
        elif type == 'conv':
            return self.get_conv(idx)
```

```python
FILTER_CONF = {
    'conv': {
        'height': 3,
        'width': 3
    },
    'maxpool': {
        'height': 2,
        'width': 2
    }
}

def apply_vgg_network_unit(x, channels, num_conv):

    """Apply VGG Network From a Convolutional Layer to Max Pooling Layer.

    Table 1 of Simonyan and Zisserman(2015) is separated by 5 parts,
    each parts is from an input data or a pooled data at previous part
    to a maxpool.
    This function provides to apply a that part.
    This will apply recursively.

    Args:
        x (Tensor)     : An input data or A Max pooled data returned by this function.
        channels (int) : A number of channels described at Table 1 of
                         Simonyan and Zisserman(2015).
        num_conv (int) : A number of applying covolutional layers.
                         See Simonyan and Zisserman(2015) for detail.

    Returns:
        A ConvNetProgressHolder object.
    """

    if num_conv < 2:
        raise ValueError('num_conv must be >= 2.')

    feature_maps = FeatureMapHolder()

    conv = calculate_convolutional_layer(
        x=x,
        filter_height=FILTER_CONF['conv']['height'],
        filter_width=FILTER_CONF['conv']['width'],
        output_channels=channels
    )
    feature_maps.set_conv(conv)

    for i in range(1, num_conv):
        conv = calculate_convolutional_layer(
            x=feature_maps.get(type='conv', idx=i-1),
            filter_height=FILTER_CONF['conv']['height'],
            filter_width=FILTER_CONF['conv']['width'],
            output_channels=channels
        )
        feature_maps.set_conv(conv)

    pool = calculate_max_pooling_layer(
        x=feature_maps.get('conv', idx=i-1),
        ksize=[1, FILTER_CONF['maxpool']['height'], FILTER_CONF['maxpool']['width'], 1],
        strides=[1, 2, 2, 1]
    )
    feature_maps.set_pool(pool)

    return feature_maps

```

### VGGの畳込みとプーリング層を構築する

VGGの論文に従い、複数回の畳込み処理と1回のMAXプーリング処理を1セットとして、それを5回繰り返す。
今回実装する画像変換アルゴリズムでは、この処理の途中経過を使うため、使いたい部分をリストで１つにまとめてsess.run()に投げる。

```python
# 例
x = tf.placeholder(tf.float32, [1, 477, 477, 3])
unit1 = apply_vgg_network_unit(x=x, channels=2, num_conv=2)
unit2 = apply_vgg_network_unit(x=unit1.get(type='pool'), channels=4, num_conv=2)
unit3 = apply_vgg_network_unit(x=unit2.get(type='pool'), channels=8, num_conv=4)
unit4 = apply_vgg_network_unit(x=unit3.get(type='pool'), channels=16, num_conv=4)
unit5 = apply_vgg_network_unit(x=unit4.get(type='pool'), channels=32, num_conv=4)
sess = tf.InteractiveSession()
sess.run(tf.global_variables_initializer())

# 使いたい過程をリストでまとめてsess.runに投げると特徴量抽出の
# 途中経過を取り出せる
result_unit2_conv, result_unit5_conv, result_unit5_pool = sess.run(
    [unit2.get(type='conv', idx=1), unit5.get(type='conv', idx=2), unit5.get(type='pool')],
    feed_dict={x: content_image}
)
```

```python
NETWORK_CONF = {
    'layer1': {
        'num_channels': 2,
        'num_conv': 2
    },
    'layer2': {
        'num_channels': 4,
        'num_conv': 2
    },
    'layer3': {
        'num_channels': 8,
        'num_conv': 4
    },
    'layer4': {
        'num_channels': 16,
        'num_conv': 4
    },
    'layer5': {
        'num_channels': 32,
        'num_conv': 4
    }
}
```

```python
def extract_feature_map(image, extract=None, run=True):

    x = tf.placeholder(tf.float32, image.shape)

    layers = []

    layer1 = apply_vgg_network_unit(
        x=x,
        channels=NETWORK_CONF['layer1']['num_channels'],
        num_conv=NETWORK_CONF['layer1']['num_conv']
    )
    layers.append(layer1)

    layer2 = apply_vgg_network_unit(
        x=layer1.get(type='pool'),
        channels=NETWORK_CONF['layer2']['num_channels'],
        num_conv=NETWORK_CONF['layer2']['num_conv']
    )
    layers.append(layer2)

    layer3 = apply_vgg_network_unit(
        x=layer2.get(type='pool'),
        channels=NETWORK_CONF['layer3']['num_channels'],
        num_conv=NETWORK_CONF['layer3']['num_conv']
    )
    layers.append(layer3)

    layer4 = apply_vgg_network_unit(
        x=layer3.get(type='pool'),
        channels=NETWORK_CONF['layer4']['num_channels'],
        num_conv=NETWORK_CONF['layer4']['num_conv']
    )
    layers.append(layer4)

    layer5 = apply_vgg_network_unit(
        x=layer4.get(type='pool'),
        channels=NETWORK_CONF['layer5']['num_channels'],
        num_conv=NETWORK_CONF['layer5']['num_conv']
    )
    layers.append(layer5)

    # モデルの構築のみの場合(run=True)はlayersを返却して終了
    if not run:
        return layers

    if extract is None:
        raise ValueError('extract is required if run is True.')

    sess = tf.InteractiveSession()
    sess.run(tf.global_variables_initializer())

    extract_layers = []
    for ext in extract:
        if len(ext) == 2:
            type, idx_layer = ext
            extract_layers.append(layers[idx_layer].get(type))
        elif len(ext) == 3:
            type, idx_layer, idx_conv = ext
            extract_layers.append(layers[idx_layer].get(type, idx_conv))
        else:
            raise ValueError('Format of extract is not available: {}'.format(ext))

    result_list = sess.run(extract_layers, feed_dict={x: image})

    return result_list

```

```python
conv1_1, conv2_2, pool4 = extract_feature_map(
    image=content_image,
    extract=[('conv', 0, 0), ('conv', 1, 1), ('pool', 3)]
)
```

# 画像を合成する処理を作る
VGGによってスタイルを変換する画像とスタイルのリファレンスとなる画像の特徴量を抽出できるようになった。
今度はこの特徴量を用いて画像を実際に合成する部分を作っていく。

このアルゴリズムは変換したいスタイルに寄せるための損失関数$L_{style}$と
変換する画像の内容に寄せるための損失関数$L_{content}$をそれぞれ計算して、
\begin{equation} L_{total} = \alpha L_{content} + \beta L_{style} \end{equation}
を最適化することで画像を合成する。

----------------------------------------
# 変換したいスタイルに寄せるための損失関数$L_{style}$を実装する
変換したいスタイルに寄せるための損失関数$L_{style}$は次で計算する：
\begin{align}
    L_{style} &= \sum_{l=1}^{L} w_{l} E_{l} \\
        E_{l} &= \frac{1}{4N_{l}^{2}M_{l}^{2}} \sum_{i=1}^{N_{l}} \sum_{j=1}^{N_{l}} (G_{ij}^{l} - A_{ij}^{l})^{2}
\end{align}
ただし、$N_{l}$は層$l$の畳込みフィルターの数、
$M_{l}$は層$l$の畳込みフィルターのサイズで$M_{l} = \text{フィルターの高さ} \times \text{フィルターの幅}$、
$A_{ij}^{l}$はスタイル画像から抽出した特徴量で計算したグラム行列$A^{l}$の$(i, j)$成分、
同様に、$G_{ij}^{l}$は合成後の画像から抽出した特徴量で計算したグラム行列$G^{l}$の$(i, j)$成分である。
$G_{ij}^{l}$は次で計算する。
$G^{l}$は$(N_{l}, N_{l})$型行列で
\begin{equation}
    G_{ij}^{l} = \sum_{k=1}^{M_{l}} F_{ik}^{l}F_{jk}^{l}.
\end{equation}

```python
def convert_nhwc_to_nchw(image):
    return image.transpose([0, 3, 1, 2])
```

```python
converted = convert_nhwc_to_nchw(result_unit5_conv)
converted.shape
```

    (1, 32, 30, 30)

```python
def flatten(image):
    num_batch, num_channel, num_height, num_width = image.shape
#    if num_batch != 1:
#        raise ValueError('Not assumed batch size has been ocurred.')
    return image.reshape([num_batch, num_channel, num_height * num_width])
```

#### flattenのテスト

```python
test_data1 = np.arange(1 * 3 * 4 * 2).reshape([1, 3, 4, 2])
test_data1
```

    array([[[[ 0,  1],
             [ 2,  3],
             [ 4,  5],
             [ 6,  7]],

            [[ 8,  9],
             [10, 11],
             [12, 13],
             [14, 15]],

            [[16, 17],
             [18, 19],
             [20, 21],
             [22, 23]]]])

```python
flatten(test_data1)
```

    array([[[ 0,  1,  2,  3,  4,  5,  6,  7],
            [ 8,  9, 10, 11, 12, 13, 14, 15],
            [16, 17, 18, 19, 20, 21, 22, 23]]])

```python
test_data2 = np.arange(2 * 3 * 4 * 2).reshape([2, 3, 4, 2])
test_data2
```

    array([[[[ 0,  1],
             [ 2,  3],
             [ 4,  5],
             [ 6,  7]],

            [[ 8,  9],
             [10, 11],
             [12, 13],
             [14, 15]],

            [[16, 17],
             [18, 19],
             [20, 21],
             [22, 23]]],

           [[[24, 25],
             [26, 27],
             [28, 29],
             [30, 31]],

            [[32, 33],
             [34, 35],
             [36, 37],
             [38, 39]],

            [[40, 41],
             [42, 43],
             [44, 45],
             [46, 47]]]])

```python
flatten(test_data2)
```

    array([[[ 0,  1,  2,  3,  4,  5,  6,  7],
            [ 8,  9, 10, 11, 12, 13, 14, 15],
            [16, 17, 18, 19, 20, 21, 22, 23]],

           [[24, 25, 26, 27, 28, 29, 30, 31],
            [32, 33, 34, 35, 36, 37, 38, 39],
            [40, 41, 42, 43, 44, 45, 46, 47]]])

```python
# グラム行列を計算する
def calculate_gram_matrix(x):
    return np.dot(x, x.T)
```

```python
flattened = flatten(result_unit5_conv)
flattened
```

    array([[[  1.72774497e+12,   1.71310606e+12,   1.71771534e+12, ...,
               2.04983711e+12,   2.06964392e+12,   2.06642859e+12],
            [  3.19720443e+12,   3.17136621e+12,   3.17231753e+12, ...,
               3.36004750e+12,   3.38134932e+12,   3.37660713e+12],
            [  4.26700073e+12,   4.23316842e+12,   4.23089118e+12, ...,
               3.69687554e+12,   3.71591007e+12,   3.71104154e+12],
            ...,
            [  4.77729286e+12,   4.74064146e+12,   4.73164310e+12, ...,
               4.39728315e+12,   4.41955333e+12,   4.41371905e+12],
            [  3.92295009e+12,   3.89338995e+12,   3.88214948e+12, ...,
               3.65187223e+12,   3.66564449e+12,   3.66082182e+12],
            [  2.26813621e+12,   2.25203690e+12,   2.23939710e+12, ...,
               2.12420749e+12,   2.12506536e+12,   2.12239201e+12]]], dtype=float32)

```python
gram_matrix = calculate_gram_matrix(flattened[0])
# gram_matrix
```

```python
gram_matrix.shape
```

    (30, 30)

```python
content_conv4_2 = extract_feature_map(image=content_image, extract=[('conv', 3, 1)])
style_conv1_1, style_conv2_1, style_conv3_1, style_conv4_1, style_conv5_1 = extract_feature_map(
    image=style_image,
    extract=[
        ('conv', 0, 0),
        ('conv', 1, 0),
        ('conv', 2, 0),
        ('conv', 3, 0),
        ('conv', 4, 0)
    ]
)
```

#### 損失関数を構成する

```python
creating_image = tf.placeholder(tf.float32, [1, 477, 477, 3])
feature_map = extract_feature_map(
    image=creating_image,
    run=False
)
```

```python
feature_map[0].get(type='conv', idx=0)
```

    <tf.Tensor 'Relu_97:0' shape=(1, 477, 477, 2) dtype=float32>

```python

```


```python
# Install qiskit library
%pip install qiskit
# Install pylatex in order to make the plots work
%pip install pylatexenc
```

    Collecting qiskit
      Downloading qiskit-0.31.0.tar.gz (13 kB)
    Collecting qiskit-terra==0.18.3
      Downloading qiskit_terra-0.18.3-cp37-cp37m-manylinux2010_x86_64.whl (6.1 MB)
         |████████████████████████████████| 6.1 MB 4.0 MB/s
    Collecting qiskit-aer==0.9.1
      Downloading qiskit_aer-0.9.1-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl (17.9 MB)
         |████████████████████████████████| 17.9 MB 114 kB/s
    Collecting qiskit-ibmq-provider==0.17.0
      Downloading qiskit_ibmq_provider-0.17.0-py3-none-any.whl (236 kB)
         |████████████████████████████████| 236 kB 73.7 MB/s
    Collecting qiskit-ignis==0.6.0
      Downloading qiskit_ignis-0.6.0-py3-none-any.whl (207 kB)
         |████████████████████████████████| 207 kB 76.2 MB/s
    Collecting qiskit-aqua==0.9.5
      Downloading qiskit_aqua-0.9.5-py3-none-any.whl (2.1 MB)
         |████████████████████████████████| 2.1 MB 27.2 MB/s
    Requirement already satisfied: scipy>=1.0 in /usr/local/lib/python3.7/dist-packages (from qiskit-aer==0.9.1->qiskit) (1.4.1)
    Requirement already satisfied: numpy>=1.16.3 in /usr/local/lib/python3.7/dist-packages (from qiskit-aer==0.9.1->qiskit) (1.19.5)
    Collecting retworkx>=0.8.0
      Downloading retworkx-0.10.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl (1.4 MB)
         |████████████████████████████████| 1.4 MB 42.2 MB/s
    Requirement already satisfied: scikit-learn>=0.20.0 in /usr/local/lib/python3.7/dist-packages (from qiskit-aqua==0.9.5->qiskit) (0.22.2.post1)
    Requirement already satisfied: fastdtw<=0.3.4 in /usr/local/lib/python3.7/dist-packages (from qiskit-aqua==0.9.5->qiskit) (0.3.4)
    Requirement already satisfied: h5py<3.3.0 in /usr/local/lib/python3.7/dist-packages (from qiskit-aqua==0.9.5->qiskit) (3.1.0)
    Requirement already satisfied: psutil>=5 in /usr/local/lib/python3.7/dist-packages (from qiskit-aqua==0.9.5->qiskit) (5.4.8)
    Collecting dlx<=1.0.4
      Downloading dlx-1.0.4.tar.gz (5.5 kB)
    Collecting quandl
      Downloading Quandl-3.6.1-py2.py3-none-any.whl (26 kB)
    Requirement already satisfied: pandas in /usr/local/lib/python3.7/dist-packages (from qiskit-aqua==0.9.5->qiskit) (1.1.5)
    Requirement already satisfied: setuptools>=40.1.0 in /usr/local/lib/python3.7/dist-packages (from qiskit-aqua==0.9.5->qiskit) (57.4.0)
    Collecting yfinance>=0.1.62
      Downloading yfinance-0.1.64.tar.gz (26 kB)
    Collecting docplex>=2.21.207
      Downloading docplex-2.22.213.tar.gz (634 kB)
         |████████████████████████████████| 634 kB 49.5 MB/s
    Requirement already satisfied: sympy>=1.3 in /usr/local/lib/python3.7/dist-packages (from qiskit-aqua==0.9.5->qiskit) (1.7.1)
    Requirement already satisfied: requests>=2.19 in /usr/local/lib/python3.7/dist-packages (from qiskit-ibmq-provider==0.17.0->qiskit) (2.23.0)
    Collecting websocket-client>=1.0.1
      Downloading websocket_client-1.2.1-py2.py3-none-any.whl (52 kB)
         |████████████████████████████████| 52 kB 2.0 MB/s
    Collecting requests-ntlm>=1.1.0
      Downloading requests_ntlm-1.1.0-py2.py3-none-any.whl (5.7 kB)
    Requirement already satisfied: urllib3>=1.21.1 in /usr/local/lib/python3.7/dist-packages (from qiskit-ibmq-provider==0.17.0->qiskit) (1.24.3)
    Requirement already satisfied: python-dateutil>=2.8.0 in /usr/local/lib/python3.7/dist-packages (from qiskit-ibmq-provider==0.17.0->qiskit) (2.8.2)
    Collecting ply>=3.10
      Downloading ply-3.11-py2.py3-none-any.whl (49 kB)
         |████████████████████████████████| 49 kB 8.0 MB/s
    Requirement already satisfied: dill>=0.3 in /usr/local/lib/python3.7/dist-packages (from qiskit-terra==0.18.3->qiskit) (0.3.4)
    Collecting tweedledum<2.0,>=1.1
      Downloading tweedledum-1.1.1-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl (943 kB)
         |████████████████████████████████| 943 kB 34.1 MB/s
    Collecting symengine>0.7
      Downloading symengine-0.8.1-cp37-cp37m-manylinux2010_x86_64.whl (38.2 MB)
         |████████████████████████████████| 38.2 MB 21 kB/s
    Collecting fastjsonschema>=2.10
      Downloading fastjsonschema-2.15.1-py3-none-any.whl (21 kB)
    Collecting python-constraint>=1.4
      Downloading python-constraint-1.4.0.tar.bz2 (18 kB)
    Requirement already satisfied: jsonschema>=2.6 in /usr/local/lib/python3.7/dist-packages (from qiskit-terra==0.18.3->qiskit) (2.6.0)
    Requirement already satisfied: six in /usr/local/lib/python3.7/dist-packages (from docplex>=2.21.207->qiskit-aqua==0.9.5->qiskit) (1.15.0)
    Requirement already satisfied: cached-property in /usr/local/lib/python3.7/dist-packages (from h5py<3.3.0->qiskit-aqua==0.9.5->qiskit) (1.5.2)
    Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.7/dist-packages (from requests>=2.19->qiskit-ibmq-provider==0.17.0->qiskit) (2021.5.30)
    Requirement already satisfied: idna<3,>=2.5 in /usr/local/lib/python3.7/dist-packages (from requests>=2.19->qiskit-ibmq-provider==0.17.0->qiskit) (2.10)
    Requirement already satisfied: chardet<4,>=3.0.2 in /usr/local/lib/python3.7/dist-packages (from requests>=2.19->qiskit-ibmq-provider==0.17.0->qiskit) (3.0.4)
    Collecting cryptography>=1.3
      Downloading cryptography-35.0.0-cp36-abi3-manylinux_2_24_x86_64.whl (3.5 MB)
         |████████████████████████████████| 3.5 MB 37.8 MB/s
    Collecting ntlm-auth>=1.0.2
      Downloading ntlm_auth-1.5.0-py2.py3-none-any.whl (29 kB)
    Requirement already satisfied: cffi>=1.12 in /usr/local/lib/python3.7/dist-packages (from cryptography>=1.3->requests-ntlm>=1.1.0->qiskit-ibmq-provider==0.17.0->qiskit) (1.14.6)
    Requirement already satisfied: pycparser in /usr/local/lib/python3.7/dist-packages (from cffi>=1.12->cryptography>=1.3->requests-ntlm>=1.1.0->qiskit-ibmq-provider==0.17.0->qiskit) (2.20)
    Requirement already satisfied: joblib>=0.11 in /usr/local/lib/python3.7/dist-packages (from scikit-learn>=0.20.0->qiskit-aqua==0.9.5->qiskit) (1.0.1)
    Requirement already satisfied: mpmath>=0.19 in /usr/local/lib/python3.7/dist-packages (from sympy>=1.3->qiskit-aqua==0.9.5->qiskit) (1.2.1)
    Requirement already satisfied: multitasking>=0.0.7 in /usr/local/lib/python3.7/dist-packages (from yfinance>=0.1.62->qiskit-aqua==0.9.5->qiskit) (0.0.9)
    Collecting lxml>=4.5.1
      Downloading lxml-4.6.3-cp37-cp37m-manylinux2014_x86_64.whl (6.3 MB)
         |████████████████████████████████| 6.3 MB 21.6 MB/s
    Requirement already satisfied: pytz>=2017.2 in /usr/local/lib/python3.7/dist-packages (from pandas->qiskit-aqua==0.9.5->qiskit) (2018.9)
    Requirement already satisfied: more-itertools in /usr/local/lib/python3.7/dist-packages (from quandl->qiskit-aqua==0.9.5->qiskit) (8.10.0)
    Collecting inflection>=0.3.1
      Downloading inflection-0.5.1-py2.py3-none-any.whl (9.5 kB)
    Building wheels for collected packages: qiskit, dlx, docplex, python-constraint, yfinance
      Building wheel for qiskit (setup.py) ... done
      Created wheel for qiskit: filename=qiskit-0.31.0-py3-none-any.whl size=11728 sha256=1e84fa48b34a9763740af95639c7fec8a111fba1ec1b3a49ecb108a7437425bc
      Stored in directory: /root/.cache/pip/wheels/29/dd/b2/1fe1a9ac92aaf75b267d893ae27329ea229f292a293017afc7
      Building wheel for dlx (setup.py) ... done
      Created wheel for dlx: filename=dlx-1.0.4-py3-none-any.whl size=5719 sha256=82f67b15af96f3a59ebffdd8a8348f334e53006e1717d67a952b604b6203d353
      Stored in directory: /root/.cache/pip/wheels/78/55/c8/dc61e772445a566b7608a476d151e9dcaf4e092b01b0c4bc3c
      Building wheel for docplex (setup.py) ... done
      Created wheel for docplex: filename=docplex-2.22.213-py3-none-any.whl size=696881 sha256=c131458598b50b288c9e529b71276448520d603459046e4e5f6e6f2837d926d6
      Stored in directory: /root/.cache/pip/wheels/90/69/6b/1375c68a5b7ff94c40263b151c86f58bd72200bf0c465b5ba3
      Building wheel for python-constraint (setup.py) ... done
      Created wheel for python-constraint: filename=python_constraint-1.4.0-py2.py3-none-any.whl size=24081 sha256=ba812ad0bb946d397ae4d888f5f4573fc8495454f48a3d916bcc5ea6b36d338a
      Stored in directory: /root/.cache/pip/wheels/07/27/db/1222c80eb1e431f3d2199c12569cb1cac60f562a451fe30479
      Building wheel for yfinance (setup.py) ... done
      Created wheel for yfinance: filename=yfinance-0.1.64-py2.py3-none-any.whl size=24109 sha256=aadd6d009376dfe92a6cb9c46f70feedb7de119b23fa1995a2d5a94b495a9563
      Stored in directory: /root/.cache/pip/wheels/86/fe/9b/a4d3d78796b699e37065e5b6c27b75cff448ddb8b24943c288
    Successfully built qiskit dlx docplex python-constraint yfinance
    Installing collected packages: tweedledum, symengine, retworkx, python-constraint, ply, fastjsonschema, qiskit-terra, ntlm-auth, lxml, inflection, cryptography, yfinance, websocket-client, requests-ntlm, quandl, qiskit-ignis, docplex, dlx, qiskit-ibmq-provider, qiskit-aqua, qiskit-aer, qiskit
      Attempting uninstall: lxml
        Found existing installation: lxml 4.2.6
        Uninstalling lxml-4.2.6:
          Successfully uninstalled lxml-4.2.6
    Successfully installed cryptography-35.0.0 dlx-1.0.4 docplex-2.22.213 fastjsonschema-2.15.1 inflection-0.5.1 lxml-4.6.3 ntlm-auth-1.5.0 ply-3.11 python-constraint-1.4.0 qiskit-0.31.0 qiskit-aer-0.9.1 qiskit-aqua-0.9.5 qiskit-ibmq-provider-0.17.0 qiskit-ignis-0.6.0 qiskit-terra-0.18.3 quandl-3.6.1 requests-ntlm-1.1.0 retworkx-0.10.2 symengine-0.8.1 tweedledum-1.1.1 websocket-client-1.2.1 yfinance-0.1.64
    Collecting pylatexenc
      Downloading pylatexenc-2.10.tar.gz (162 kB)
         |████████████████████████████████| 162 kB 5.0 MB/s
    Building wheels for collected packages: pylatexenc
      Building wheel for pylatexenc (setup.py) ... done
      Created wheel for pylatexenc: filename=pylatexenc-2.10-py3-none-any.whl size=136833 sha256=4156775a6299f6b21491544ef2bff72d2cef11786591eea3ee3854bcfbbbaee7
      Stored in directory: /root/.cache/pip/wheels/f1/8a/f5/33ee79d4473eb201b519fa40f989b842e373237395a3421f52
    Successfully built pylatexenc
    Installing collected packages: pylatexenc
    Successfully installed pylatexenc-2.10

```python
# Import numpy, matplotlib, qiskit
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
from qiskit import *
from qiskit.compiler import transpile, assemble
from qiskit.tools.visualization import *
from qiskit.tools.jupyter import *
from qiskit.visualization import *

from math import *

# Definition of SPIN correlation with qbits array format in quiskit for more clarity
SPIN_DOWN = [1,0]
SPIN_UP = [0,1]
```

## Circuits

Here we define the functions which return the circuits

```python
def Nodo_exec_counts(circ, shots=1000):
  """
  Get the get_count() object of quiskit when execute specific number of shots.
  Executed in 'qasm_simulator' backend.
  Args:
    circ: QuantumCircuit to execute
    shots: number of executions, default=1000

  Return:
    get_count() object of quiskit
  """
  backend = BasicAer.get_backend('qasm_simulator') # the device to run on
  result = execute(circ, backend, shots=shots).result().get_counts()
  #print(result)
  return result

def Nodo_exec(circ):
  """
  Get the qbit value in output when executed 1 shot.
  Executed in 'qasm_simulator' backend.
  Args:
    circ: QuantumCircuit to execute

  Return:
    SPIN_DOWN or SPIN_UP matrix
  """
  backend = BasicAer.get_backend('qasm_simulator') # the device to run on
  result = execute(circ, backend, shots=1).result().get_counts()
  #print(result)
  return SPIN_DOWN if "0" in result and result["0"]==1 else SPIN_UP
```

### Ising Without Magnetism

```python
def ISING_GATE():
  """
  Define the ISING circuit without setting the inputs for be used as sub_circuit
  or gate in others circuits

  Return:
    ISING circuite

  """
  circ = QuantumCircuit(5, name="ISING")
  #circ.initialize(SPIN_DOWN, 3)

  circ.mcx([0,1,2],3)
  #######
  circ.barrier()
  circ.x(0)
  circ.x(1)
  circ.x(2)
  circ.mcx([0,1,2],3)
  circ.x(0)
  circ.x(1)
  circ.x(2)
  circ.barrier()
  ######
  circ.x(3)
  circ.cx(3,0)
  circ.x(3)
  circ.barrier()
  #######
  circ.ccx(3,4,0)
  return circ

def ISING(S,A,B,P):
  """
  Define the ISING circuit and setting the inputs for be executed
  or gate in others circuits. Depends on the Temperature.

  ARGS:
    S: state of spin to execute and measure
    A: state of the left neighbour
    B: state of the right neighbour
    P: [sqrt(1-P), sqrt(P)]. P depends on T

  Return:
    ISING circuite with initial states defines

  """
  circ = QuantumCircuit(5, 1, name="ISING_GATE")
  circ.initialize(SPIN_DOWN, 3)
  circ.initialize(S, 0)
  circ.initialize(A,1)
  circ.initialize(B,2)
  circ.initialize(P[0],4)

  circ.mcx([0,1,2],3)
  #######
  circ.barrier()
  circ.x(0)
  circ.x(1)
  circ.x(2)
  circ.mcx([0,1,2],3)
  circ.x(0)
  circ.x(1)
  circ.x(2)
  circ.barrier()
  ######
  circ.x(3)
  circ.cx(3,0)
  circ.x(3)
  circ.barrier()
  #######
  circ.ccx(3,4,0)
  circ.measure(0, 0)
  return circ
```

```python
P = exp(-4/3)
P_calc = [sqrt(1-P), sqrt(P)]
circ = ISING(SPIN_UP,SPIN_UP,SPIN_UP, [P_calc])
circ.draw(output='mpl')
```

```python
result = Nodo_exec_counts(circ)
print(result)
plot_histogram(result)
```

### Ising Magnetism

```python
def getP_i(T,h):
  """
  Calculate the hopping probabilities

  ARGS:
    T: Temperature
    h: Magnetic field

  Return:
    P1 = min(1, np.exp(-1*(4-2*h)/T))
    P2 = min(1, np.exp (  (4-2*h)/T  ))
    P3 = np.exp(-2*h/T)
    P4 = np.exp(-1*(4+2*h)/T)

  """
  p1 = min(1, np.exp(-1*(4-2*h)/T))
  p2 = min(1, np.exp (  (4-2*h)/T  ))
  p3 = np.exp(-2*h/T)
  p4 = np.exp(-1*(4+2*h)/T)
  P1 = [np.sqrt(1-p1), np.sqrt(p1)]
  P2 = [np.sqrt(1-p2), np.sqrt(p2)]
  P3 = [np.sqrt(1-p3), np.sqrt(p3)]
  P4 = [np.sqrt(1-p4), np.sqrt(p4)]
  return P1, P2, P3, P4
```

```python
def ISINH(S,A,B,P1,P2,P3,P4):
  """
  Define the ISINH circuit and setting the inputs for be executed
  or gate in others circuits. Depend on the Temperature and Magnetic field

  ARGS:
    S: state of spin to execute and measure
    A: state of the left neighbour
    B: state of the right neighbour
    P1 = min(1, np.exp(-1*(4-2*h)/T))
    P2 = min(1, np.exp (  (4-2*h)/T  ))
    P3 = np.exp(-2*h/T)
    P4 = np.exp(-1*(4+2*h)/T)

  Return:
    ISINH circuite with initial states defines

  """
  circ = QuantumCircuit(9, 1)

  circ.initialize(S, 0)
  circ.initialize(A,1)
  circ.initialize(B,2)
  circ.initialize(SPIN_DOWN, 3)
  circ.initialize(SPIN_DOWN, 4)

  circ.initialize(P1,5)
  circ.initialize(P2,6)
  circ.initialize(P3,7)
  circ.initialize(P4,8)

  circ.mcx([0,1,2],3)

  circ.x(0)
  circ.x(1)
  circ.x(2)
  circ.mcx([0,1,2],3)
  circ.x(0)
  circ.x(1)
  circ.x(2)
  circ.barrier()
  ######
  circ.x(1)
  circ.mcx([0,1,2], 4)
  circ.x(1)
  circ.x(2)
  circ.mcx([0,1,2], 4)
  circ.x(2)
  circ.barrier()
  ######
  circ.x(2)
  circ.mcx([2,3,5],0)
  circ.x(2)
  circ.mcx([2,3,8],0)

  circ.barrier()
  ######

  circ.mcx([4,7], 0)

  circ.x(1)
  circ.x(2)
  circ.x(3)
  circ.mcx([1,2,3,6], 0)
  circ.x(1)
  circ.x(2)
  circ.x(3)
  circ.barrier()

  ######

  circ.x(3)
  circ.x(4)
  circ.mcx([2,3,4],0)
  circ.x(3)
  circ.x(4)

  circ.x(3)
  circ.x(4)
  circ.mcx([1,3,4],0)
  circ.x(3)
  circ.x(4)

  circ.x(3)
  circ.x(4)
  circ.mcx([1,2,3,4],0)
  circ.x(3)
  circ.x(4)

  circ.barrier()
  ######
  circ.measure(0, 0)
  return circ
```

```python
P1,P2,P3,P4 = getP_i(T=1,h=1)
circ = ISINH(SPIN_UP,SPIN_UP,SPIN_UP,P1,P2,P3,P4)
circ.draw(output='mpl')
```

```python
result = Nodo_exec_counts(circ)
print(result)
plot_histogram(result)
```

### Isign One Measure

```python
def ISING_one_measure(S,A,B,P,M,generations=1):
  """
  Define ISING circuit design specifically to 4 spins and avoid measure in each
  generation only in the end. We define a cyclic circuite which

  ARGS:
    S: state of spin to execute and measure
    A: state of the left neighbour
    B: state of the right neighbour
    P1 = min(1, np.exp(-1*(4-2*h)/T))
    P2 = min(1, np.exp (  (4-2*h)/T  ))
    P3 = np.exp(-2*h/T)
    P4 = np.exp(-1*(4+2*h)/T)

  Return:
    ISINH circuite with initial states defines

  """
  circ = QuantumCircuit(6, 4, name="ISING_no_measure")

  circ.initialize(S, 0)
  circ.initialize(A,1)
  circ.initialize(B,2)
  circ.initialize(SPIN_DOWN, 3)
  circ.initialize(P,4)
  circ.initialize(M,5)

  for i in range(generations):
    ising = ISING_GATE().to_instruction()
    circ.append(ising, [0,1,2,3,4])
    #BLOQUE DE ISING
    circ.swap(0,5)
    circ.swap(1,2)
    circ.append(ising, [0,1,2,3,4])
    #BLOQUE DE ISING
    circ.swap(0,1)
    circ.swap(1,2)
    circ.swap(1,5)
    circ.append(ising, [0,1,2,3,4])
    #BLOQUE ISING
    circ.swap(1,2)
    circ.swap(0,5)
    circ.append(ising, [0,1,2,3,4])
    #BLOQUE ISING
    circ.swap(0,2)
    circ.swap(1,2)
    circ.swap(2,5)

  ###### Hay que hacer un measure de todos los qubits, importante meterlo al final
  circ.measure(0, 0)
  circ.measure(1, 3)
  circ.measure(2, 1)
  circ.measure(5, 2)
  return circ
```

```python
circ = ISING_one_measure(SPIN_DOWN,SPIN_DOWN,SPIN_DOWN,SPIN_DOWN,SPIN_DOWN, generations=1)
circ.draw(output='mpl')
```

```python
circ = ISING_one_measure(SPIN_DOWN,SPIN_DOWN,SPIN_DOWN,SPIN_DOWN,SPIN_DOWN, generations=2)
circ.draw(output='mpl')
```

## Ising Loop Implementations

```python
# Auxiliar function to print spin as rows
def qbit_to_arrow(S):
  """
  Print qbit or spin in arrow unicode format

  ARGS:
    S: qbit or spin array format. Ej: [0,0]

  Return:
    Unicode arrow

  Raise:
    Exception when the spin is not [0,1] or [1,0]
  """
  arrows = []
  for i in S:
    if i == SPIN_UP:
      arrows.append(u"\u2191")
    elif i == SPIN_DOWN:
      arrows.append(u"\u2193")
    else:
      print(i)
      raise Exception("Not SPIN_DOWN or SPIN_UP")

  return arrows
```

### Ising 1D

#### Without Magnetism

```python
def set_neighbours(S):
  """
  Return:
    As: Left neightbour
    As: Left neightbour
  """
  As = left_neighbours(S)
  Bs = right_neighbours(S)

  return As, Bs

def left_neighbours(Ss):
  left = []
  for i in range(len(Ss)):
    left.append(Ss[(i-1)%len(Ss)])

  return left

def right_neighbours(Ss):
  right = []
  for i in range(len(Ss)):
    right.append(Ss[(i+1)%len(Ss)])

  return right
```

```python
def generation_update(circuit_def, Ss, P):
  As, Bs = set_neighbours(Ss)
  return {i: circuit_def(Ss[i], As[i], Bs[i], P) for i in range(len(Ss))}

def Ising(T, Ss=[SPIN_DOWN,SPIN_DOWN,SPIN_DOWN,SPIN_DOWN], generations=10):

  As, Bs = set_neighbours(Ss)

  #print("As Bs", As, Bs)

  P = exp(-4/T)
  P_calc = [sqrt(1-P), sqrt(P)]

  circuit_def = ISING

  circuitos = generation_update(circuit_def, Ss, [P_calc])

  spin = []
  #print('Spin Incial:', Ss)
  for i in range(generations):
    #print("Generation", i)
    for j in range(0,len(Ss),2):
      Ss[j] = Nodo_exec(circuitos[j])
    circuitos = generation_update(circuit_def, Ss, [P_calc])

    #print('\tSpin:', Ss)

    for j in range(1,len(Ss),2):
      Ss[j] = Nodo_exec(circuitos[j])
    circuitos = generation_update(circuit_def, Ss, [P_calc])

    #print('\tSpin:', Ss)

    spin.append((sum([k[1] for k in Ss])*1 - sum([k[0] for k in Ss])) / len(Ss))

  return Ss, sum(spin)/generations

def Ising_with_warmup(T, Ss=[SPIN_DOWN,SPIN_DOWN,SPIN_DOWN,SPIN_DOWN], num_warmup=10, generations=10):
  Ss, avg_up = Ising(T, Ss, generations=num_warmup) # calentamiento
  Ss, avg_up = Ising(T, Ss, generations=generations)
  return Ss, avg_up
```

```python
S = [SPIN_DOWN,SPIN_DOWN,SPIN_DOWN,SPIN_DOWN,SPIN_DOWN,SPIN_DOWN,SPIN_DOWN,SPIN_DOWN]
spins, avg_up = Ising_with_warmup(T=1, Ss=S, num_warmup=50, generations=50)
```

```python
print(spins)
print(qbit_to_arrow(spins))
print('AVG SPIN_UP:', avg_up)
```

    [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    ['↑', '↑', '↑', '↑', '↑', '↑', '↑', '↑']
    AVG SPIN_UP: 0.25

```python
S = [SPIN_DOWN,SPIN_DOWN,SPIN_DOWN,SPIN_DOWN,SPIN_DOWN,SPIN_DOWN,SPIN_DOWN,SPIN_DOWN]
avg_up_vec = []
Ts = np.linspace(0.1,2,20)
for i in Ts:
  _ , avg_up = Ising_with_warmup(T=i, Ss=S, num_warmup=50, generations=50)
  avg_up_vec.append(avg_up)
```

    As Bs [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]] [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    Spin Incial: [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    As Bs [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]] [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    Spin Incial: [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    As Bs [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]] [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    Spin Incial: [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    As Bs [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]] [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    Spin Incial: [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    As Bs [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]] [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    Spin Incial: [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    As Bs [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]] [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    Spin Incial: [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    As Bs [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]] [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    Spin Incial: [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    As Bs [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]] [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    Spin Incial: [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    As Bs [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]] [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    Spin Incial: [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    As Bs [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]] [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    Spin Incial: [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    As Bs [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]] [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    Spin Incial: [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    As Bs [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1]] [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    Spin Incial: [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    As Bs [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1]] [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    Spin Incial: [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    As Bs [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]] [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    Spin Incial: [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    As Bs [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]] [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    Spin Incial: [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    As Bs [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]] [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    Spin Incial: [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    As Bs [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]] [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    Spin Incial: [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    As Bs [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1]] [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    Spin Incial: [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    As Bs [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]] [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    Spin Incial: [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    As Bs [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1]] [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    Spin Incial: [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    As Bs [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]] [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    Spin Incial: [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    As Bs [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1]] [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    Spin Incial: [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    As Bs [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]] [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    Spin Incial: [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    As Bs [[1, 0], [0, 1], [0, 1], [0, 1], [1, 0], [1, 0], [1, 0], [1, 0]] [[0, 1], [0, 1], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [0, 1]]
    Spin Incial: [[0, 1], [0, 1], [0, 1], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    As Bs [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]] [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    Spin Incial: [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    As Bs [[0, 1], [0, 1], [0, 1], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]] [[0, 1], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [0, 1], [0, 1]]
    Spin Incial: [[0, 1], [0, 1], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [0, 1]]
    As Bs [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]] [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    Spin Incial: [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    As Bs [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1]] [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    Spin Incial: [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    As Bs [[0, 1], [1, 0], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1]] [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [1, 0]]
    Spin Incial: [[1, 0], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    As Bs [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1]] [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    Spin Incial: [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    As Bs [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]] [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    Spin Incial: [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    As Bs [[1, 0], [0, 1], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]] [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [0, 1]]
    Spin Incial: [[0, 1], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    As Bs [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]] [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    Spin Incial: [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    As Bs [[1, 0], [1, 0], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [1, 0]] [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [1, 0], [1, 0], [1, 0]]
    Spin Incial: [[1, 0], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [1, 0], [1, 0]]
    As Bs [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [1, 0], [1, 0], [1, 0]] [[0, 1], [0, 1], [0, 1], [1, 0], [1, 0], [1, 0], [0, 1], [0, 1]]
    Spin Incial: [[0, 1], [0, 1], [0, 1], [0, 1], [1, 0], [1, 0], [1, 0], [0, 1]]
    As Bs [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]] [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    Spin Incial: [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    As Bs [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1]] [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    Spin Incial: [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    As Bs [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1]] [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    Spin Incial: [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    As Bs [[0, 1], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]] [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [0, 1], [1, 0]]
    Spin Incial: [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [0, 1]]
    As Bs [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1]] [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    Spin Incial: [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]

```python
plt.plot(Ts, avg_up_vec, '.')
plt.xlabel("Temperature (J/K)", fontsize = 14)
plt.ylabel("Spin", fontsize = 14)
plt.title("Spin dependence of temperature", fontsize = 14)
```

#### With Magnetism

```python
def generation_update_mag(circuit_def, Ss, P):
  As, Bs = set_neighbours(Ss)
  return {i: circuit_def(Ss[i], As[i], Bs[i], P[0], P[1], P[2], P[3]) for i in range(len(Ss))}
```

```python
def Ising_Magnetic(h, T, Ss=[SPIN_DOWN,SPIN_DOWN,SPIN_DOWN,SPIN_DOWN], generations=10):

  As, Bs = set_neighbours(Ss)

  #print("As Bs", As, Bs)

  P1, P2, P3, P4 = getP_i(T,h)

  P = [P1, P2, P3, P4]

  circuit_def = ISINH

  circuitos = generation_update_mag(circuit_def, Ss, P)

  spin = []
  #print('Spin Incial:', Ss)
  for i in range(generations):
    #print("Generation", i)
    for j in range(0,len(Ss),2):
      Ss[j] = Nodo_exec(circuitos[j])
    circuitos = generation_update_mag(circuit_def, Ss, P)

    #print('\tSpin:', Ss)

    for j in range(1,len(Ss),2):
      Ss[j] = Nodo_exec(circuitos[j])
    circuitos = generation_update_mag(circuit_def, Ss, P)

    #print('\tSpin:', Ss)

    spin.append((sum([k[1] for k in Ss])*1 - sum([k[0] for k in Ss])) / len(Ss))

  return Ss, sum(spin)/generations

def Ising_Magnetic_with_warmup(h, T, Ss=[SPIN_DOWN,SPIN_DOWN,SPIN_DOWN,SPIN_DOWN], num_warmup=5, generations=10):
  Ss, _ = Ising_Magnetic(h, T, Ss, generations=num_warmup)
  Ss, avg_up = Ising_Magnetic(h, T, Ss, generations=generations)
  return Ss, avg_up
```

```python
spins, avg_up = Ising_Magnetic(h=1, T=1, Ss=[SPIN_DOWN,SPIN_DOWN,SPIN_DOWN,SPIN_DOWN], generations=10)
```

    As Bs [[1, 0], [1, 0], [1, 0], [1, 0]] [[1, 0], [1, 0], [1, 0], [1, 0]]
    Spin Incial: [[1, 0], [1, 0], [1, 0], [1, 0]]

```python
print(spins)
print(qbit_to_arrow(spins))
print('AVG SPIN_UP:', avg_up)
```

    [[0, 1], [0, 1], [0, 1], [0, 1]]
    ['↑', '↑', '↑', '↑']
    AVG SPIN_UP: 0.95

```python
spins, avg_up = Ising_Magnetic_with_warmup(h=1, T=1, Ss=[SPIN_DOWN,SPIN_DOWN,SPIN_DOWN,SPIN_DOWN], num_warmup=5, generations=10)
```

```python
print(spins)
print(qbit_to_arrow(spins))
print('AVG SPIN_UP:', avg_up)
```

    [[0, 1], [0, 1], [0, 1], [0, 1]]
    ['↑', '↑', '↑', '↑']
    AVG SPIN_UP: 1.0

```python
#We use low temperatures to see the how the function becomes step at h=0
S = [SPIN_DOWN,SPIN_DOWN,SPIN_DOWN,SPIN_DOWN,SPIN_DOWN]
T_vec = [0.1, 1, 5]
h_num = 10
h_vec = np.linspace(0.3, 2, h_num)
avg_spin_vec = np.zeros([len(T_vec),h_num])
for i in range(len(T_vec)):
  for j in range(h_num):
    _ , avg_spin_vec[i, j] = Ising_Magnetic_with_warmup(h_vec[j], T_vec[i], Ss=S,
                                                 num_warmup=5, generations=5)
```

    As Bs [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]] [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    Spin Incial: [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    As Bs [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]] [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    Spin Incial: [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    As Bs [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]] [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    Spin Incial: [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    As Bs [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]] [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    Spin Incial: [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    As Bs [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]] [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    Spin Incial: [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    As Bs [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]] [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    Spin Incial: [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    As Bs [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]] [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    Spin Incial: [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    As Bs [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]] [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    Spin Incial: [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    As Bs [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]] [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    Spin Incial: [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    As Bs [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]] [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    Spin Incial: [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    As Bs [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]] [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    Spin Incial: [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    As Bs [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]] [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    Spin Incial: [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    As Bs [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]] [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    Spin Incial: [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    As Bs [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]] [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    Spin Incial: [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    As Bs [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]] [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    Spin Incial: [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    As Bs [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]] [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    Spin Incial: [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    As Bs [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]] [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    Spin Incial: [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    As Bs [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]] [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    Spin Incial: [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    As Bs [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]] [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    Spin Incial: [[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
    As Bs [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]] [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    Spin Incial: [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    As Bs [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]] [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    Spin Incial: [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    As Bs [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]] [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    Spin Incial: [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    As Bs [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]] [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    Spin Incial: [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    As Bs [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]] [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    Spin Incial: [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    As Bs [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]] [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    Spin Incial: [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    As Bs [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]] [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    Spin Incial: [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    As Bs [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]] [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    Spin Incial: [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    As Bs [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]] [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    Spin Incial: [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    As Bs [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]] [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    Spin Incial: [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    As Bs [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]] [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    Spin Incial: [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    As Bs [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]] [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    Spin Incial: [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    As Bs [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]] [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    Spin Incial: [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    As Bs [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]] [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    Spin Incial: [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    As Bs [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]] [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    Spin Incial: [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    As Bs [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]] [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    Spin Incial: [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    As Bs [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]] [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    Spin Incial: [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    As Bs [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]] [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    Spin Incial: [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    As Bs [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]] [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    Spin Incial: [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    As Bs [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]] [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    Spin Incial: [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    As Bs [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]] [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    Spin Incial: [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    As Bs [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]] [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    Spin Incial: [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    As Bs [[0, 1], [1, 0], [1, 0], [1, 0], [0, 1]] [[1, 0], [1, 0], [0, 1], [0, 1], [1, 0]]
    Spin Incial: [[1, 0], [1, 0], [1, 0], [0, 1], [0, 1]]
    As Bs [[0, 1], [0, 1], [1, 0], [1, 0], [1, 0]] [[1, 0], [1, 0], [1, 0], [0, 1], [0, 1]]
    Spin Incial: [[0, 1], [1, 0], [1, 0], [1, 0], [0, 1]]
    As Bs [[1, 0], [0, 1], [1, 0], [0, 1], [1, 0]] [[1, 0], [0, 1], [1, 0], [1, 0], [0, 1]]
    Spin Incial: [[0, 1], [1, 0], [0, 1], [1, 0], [1, 0]]
    As Bs [[0, 1], [1, 0], [1, 0], [1, 0], [0, 1]] [[1, 0], [1, 0], [0, 1], [0, 1], [1, 0]]
    Spin Incial: [[1, 0], [1, 0], [1, 0], [0, 1], [0, 1]]
    As Bs [[1, 0], [0, 1], [0, 1], [0, 1], [1, 0]] [[0, 1], [0, 1], [1, 0], [1, 0], [0, 1]]
    Spin Incial: [[0, 1], [0, 1], [0, 1], [1, 0], [1, 0]]
    As Bs [[1, 0], [0, 1], [0, 1], [0, 1], [0, 1]] [[0, 1], [0, 1], [0, 1], [1, 0], [0, 1]]
    Spin Incial: [[0, 1], [0, 1], [0, 1], [0, 1], [1, 0]]
    As Bs [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]] [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    Spin Incial: [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    As Bs [[0, 1], [1, 0], [1, 0], [1, 0], [1, 0]] [[1, 0], [1, 0], [1, 0], [0, 1], [1, 0]]
    Spin Incial: [[1, 0], [1, 0], [1, 0], [1, 0], [0, 1]]
    As Bs [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]] [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    Spin Incial: [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    As Bs [[0, 1], [1, 0], [0, 1], [0, 1], [1, 0]] [[0, 1], [0, 1], [1, 0], [0, 1], [1, 0]]
    Spin Incial: [[1, 0], [0, 1], [0, 1], [1, 0], [0, 1]]
    As Bs [[0, 1], [0, 1], [1, 0], [0, 1], [0, 1]] [[1, 0], [0, 1], [0, 1], [0, 1], [0, 1]]
    Spin Incial: [[0, 1], [1, 0], [0, 1], [0, 1], [0, 1]]
    As Bs [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]] [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    Spin Incial: [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    As Bs [[0, 1], [1, 0], [1, 0], [1, 0], [0, 1]] [[1, 0], [1, 0], [0, 1], [0, 1], [1, 0]]
    Spin Incial: [[1, 0], [1, 0], [1, 0], [0, 1], [0, 1]]
    As Bs [[0, 1], [1, 0], [0, 1], [0, 1], [0, 1]] [[0, 1], [0, 1], [0, 1], [0, 1], [1, 0]]
    Spin Incial: [[1, 0], [0, 1], [0, 1], [0, 1], [0, 1]]
    As Bs [[0, 1], [0, 1], [1, 0], [1, 0], [0, 1]] [[1, 0], [1, 0], [0, 1], [0, 1], [0, 1]]
    Spin Incial: [[0, 1], [1, 0], [1, 0], [0, 1], [0, 1]]
    As Bs [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]] [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    Spin Incial: [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    As Bs [[0, 1], [0, 1], [1, 0], [0, 1], [0, 1]] [[1, 0], [0, 1], [0, 1], [0, 1], [0, 1]]
    Spin Incial: [[0, 1], [1, 0], [0, 1], [0, 1], [0, 1]]
    As Bs [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]] [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    Spin Incial: [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
    As Bs [[0, 1], [1, 0], [0, 1], [0, 1], [0, 1]] [[0, 1], [0, 1], [0, 1], [0, 1], [1, 0]]
    Spin Incial: [[1, 0], [0, 1], [0, 1], [0, 1], [0, 1]]

```python
for i in range(len(T_vec)):
  plt.plot(h_vec, avg_spin_vec[i, :], '.')
plt.xlabel("Magnetic field (J)", fontsize = 14)
plt.ylabel("Spin", fontsize = 14)
plt.title("Spin dependence of magnetic field", fontsize = 14)
plt.legend(["T = 0.1", "T = 1", "T = 5"])
```