---
# System prepended metadata

title: '`core.py` Cheat Sheet!'

---

# `core.py` Cheat Sheet!

This document can serve as a quick cheat sheet for the various moving classes throughout the `beras` Module!

## Classes in `core.py`:

### `Tensor` (line 7)

 Essentially, a NumPy Array that can also be marked as trainable.

An instance of the `Tensor` class will be a NumPy array, but you can access the `.trainable` boolean value to check if the array is trainable or not. 

### `Variable` (line 25)

an alias for the `Tensor` class

### `Callable` (line 29)

Say you have an instance `layer1` of class `Layer` which extends `Callable`. Calling `layer1(argument)` will be the same as calling `layer1.call(argument)`

#### Methods:

* `__call__(self, *args, **kwargs) -> Tensor:`

Calls the `call()` method with `*args` and `**kwargs**` as arguments. Then, casts output to `Tensor` class and returns.

* `call(self, *args, **kwargs)`

Abstract method

### `Weighted` (line 48)

Abstract class meant to represent any Module that has inherent weights that can be trained. 

For instance, a Linear/Dense layer has a weight and bias, and thus should subclass `Weighted`

#### Methods:

* `weights(self) -> list[Tensor]`

Abstract method. Intended for the subclass to return the instance's weights. 

e.g. a Linear layer's `weights` method should return that layer's weight and bias.

* `trainable_variables(self) -> list[Tensor]:`

Returns a list of all weights which are trainable 

* `non_trainable_variables(self) -> list[Tensor]:`

Returns a list of all weights which are _not_ trainable

* `trainable(self) -> bool:`

Returns `True` if there are any trainable `Tensor`s. Returns `False` otherwise.

* `trainable(self, value: bool):`

Sets alll trainable weights to `value`.

Mainly will be used to denote Weighted things that should not be trained (by calling `trainable(value=False)`)

### `Diffable` (line 79)

`Diffable` subclasses `Callable` and `Weighted`. This means that `Diffable` things should have a call function and weights of some kind. 

For any class (say `DiffableThing`) which extends `Diffable`, using the `__call__` method will build up a lot of useful instance variables based on the `DiffableThing`'s `call` function.

That is to say, say we have `thingInstance = DiffableThing()`. Using `thingInstance(arguments)` will populate `thingInstance`'s instance variables based on the `thingInstance.call` method definition.

#### Class Variables

* `gradient_tape`

You don't need to worry about interfacing with this variable yourself. However, this effectively keeps track of whether there is a GradientTape that is keeping track of `Diffable` operations or not. 

You'll get very used to seeing something like:

```python
with GradientTape as tape:
  #do some diffable things
  #do some more diffable things
#do some non-diffable things
```

This basically creates a GradientTape to keep track of `Diffable` operations when in the scope of that tape (e.g. lines 2-3). You exit the scope (like exiting an `if` statement) by leaving its indentation level (e.g. `tape` will not record any operations starting from line 4).

If the `Diffable` class has a `gradient_tape` that is not `None`, then all instances of classes which extend `Diffable` will record their operations to that `gradient_tape`.

#### Instance Variables

* `argnames`

List of the names of all arguments to the `call` function which are not keyword arguments. 

You probably won't need to use this

* `input_dict`

A dictionary which associates all arguments to the `call` function by storing: {`arg_name`: `arg_value`}

* `inputs`

A list which contains the values passed in as arguments to the `call` function.

* `outputs`

A list which contains all values returned by the `call` function.

#### Methods

* `get_input_gradients(self) -> list[np.ndarray]:`

Abstract method. Returns list of gradients with respect to inputs.

* `get_weight_gradients(self) -> list[np.ndarray]:`

Abstract method. Returns list of gradients with respect to weights.

* `compose_input_gradients(self, J=None):`

Composes (multiplies) the inputted cumulative jacobian matrix (a matrix associating partial derivatives for output variables with respect to input variables) with the input jacobian matrix for the layer.

* `compose_weight_gradients(self, J=None) -> list:`

Composes (multiplies) the inputted cumulative jacobian matrix with the weight jacobian for the layer.

