Packages

A self-modifying MetaDSL system for interacting with LM Studio

Current section

Files

Jump to
lmstudio lib lmstudio neural_architecture.ex
Raw

lib/lmstudio/neural_architecture.ex

defmodule LMStudio.NeuralArchitecture do
@moduledoc """
Advanced Neural Architecture for Cognitive Agents
Implements self-modifying neural networks, attention mechanisms,
transformer architectures, and neural evolution strategies.
"""
use GenServer
require Logger
defmodule Neuron do
@moduledoc "Individual neuron with adaptive properties"
defstruct [
:id,
:weights,
:bias,
:activation_function,
:learning_rate,
:momentum,
:dropout_rate,
:last_activation,
:gradient_accumulator,
:adaptation_history,
:plasticity_factor
]
@type t :: %__MODULE__{
id: String.t(),
weights: %{String.t() => float()},
bias: float(),
activation_function: atom(),
learning_rate: float(),
momentum: %{String.t() => float()},
dropout_rate: float(),
last_activation: float(),
gradient_accumulator: %{String.t() => float()},
adaptation_history: [map()],
plasticity_factor: float()
}
def new(id, input_connections \\ [], opts \\ []) do
activation_fn = Keyword.get(opts, :activation_function, :tanh)
learning_rate = Keyword.get(opts, :learning_rate, 0.001)
dropout_rate = Keyword.get(opts, :dropout_rate, 0.1)
weights = Map.new(input_connections, fn connection_id ->
# Xavier/Glorot initialization
limit = :math.sqrt(6.0 / (length(input_connections) + 1))
weight = (:rand.uniform() * 2 - 1) * limit
{connection_id, weight}
end)
%__MODULE__{
id: id,
weights: weights,
bias: (:rand.uniform() * 2 - 1) * 0.1,
activation_function: activation_fn,
learning_rate: learning_rate,
momentum: Map.new(input_connections, &{&1, 0.0}),
dropout_rate: dropout_rate,
last_activation: 0.0,
gradient_accumulator: Map.new(input_connections, &{&1, 0.0}),
adaptation_history: [],
plasticity_factor: 1.0
}
end
def forward(%__MODULE__{} = neuron, inputs, training? \\ false) do
# Apply dropout during training
if training? and :rand.uniform() < neuron.dropout_rate do
{0.0, neuron}
else
# Calculate weighted sum
weighted_sum = Enum.reduce(inputs, neuron.bias, fn {input_id, value}, acc ->
weight = Map.get(neuron.weights, input_id, 0.0)
acc + weight * value
end)
# Apply activation function
activation = apply_activation(weighted_sum, neuron.activation_function)
# Update neuron state
updated_neuron = %{neuron | last_activation: activation}
{activation, updated_neuron}
end
end
def backward(%__MODULE__{} = neuron, error_gradient, inputs) do
# Calculate gradients using backpropagation
activation_derivative = activation_derivative(neuron.last_activation, neuron.activation_function)
local_gradient = error_gradient * activation_derivative
# Calculate weight gradients
weight_gradients = Map.new(inputs, fn {input_id, value} ->
gradient = local_gradient * value
{input_id, gradient}
end)
# Calculate bias gradient
bias_gradient = local_gradient
# Update weights with momentum and learning rate
updated_weights = Map.new(neuron.weights, fn {input_id, weight} ->
gradient = Map.get(weight_gradients, input_id, 0.0)
momentum_term = Map.get(neuron.momentum, input_id, 0.0)
# Momentum update
new_momentum = 0.9 * momentum_term + neuron.learning_rate * gradient
new_weight = weight - new_momentum
{input_id, new_weight}
end)
# Update bias
updated_bias = neuron.bias - neuron.learning_rate * bias_gradient
# Update momentum
updated_momentum = Map.new(weight_gradients, fn {input_id, gradient} ->
momentum_term = Map.get(neuron.momentum, input_id, 0.0)
new_momentum = 0.9 * momentum_term + neuron.learning_rate * gradient
{input_id, new_momentum}
end)
# Accumulate gradients for analysis
updated_accumulator = Map.merge(neuron.gradient_accumulator, weight_gradients, fn _k, old, new ->
old + new
end)
# Record adaptation
adaptation_record = %{
timestamp: DateTime.utc_now(),
weight_updates: map_size(weight_gradients),
avg_gradient_magnitude: calculate_avg_magnitude(weight_gradients),
learning_rate: neuron.learning_rate
}
updated_neuron = %{neuron |
weights: updated_weights,
bias: updated_bias,
momentum: updated_momentum,
gradient_accumulator: updated_accumulator,
adaptation_history: [adaptation_record | Enum.take(neuron.adaptation_history, 99)]
}
# Return input gradients for further backpropagation
input_gradients = Map.new(inputs, fn {input_id, _value} ->
weight = Map.get(neuron.weights, input_id, 0.0)
{input_id, local_gradient * weight}
end)
{input_gradients, updated_neuron}
end
def adapt_learning_rate(%__MODULE__{} = neuron) do
# Adaptive learning rate based on gradient history
if length(neuron.adaptation_history) >= 2 do
[current | [previous | _]] = neuron.adaptation_history
# If gradients are consistently large, reduce learning rate
# If gradients are small, increase learning rate
gradient_ratio = current.avg_gradient_magnitude / max(previous.avg_gradient_magnitude, 1.0e-8)
new_learning_rate = cond do
gradient_ratio > 2.0 -> neuron.learning_rate * 0.9 # Reduce if gradients exploding
gradient_ratio < 0.5 -> neuron.learning_rate * 1.05 # Increase if gradients vanishing
true -> neuron.learning_rate
end
# Clamp learning rate
clamped_learning_rate = max(min(new_learning_rate, 0.1), 1.0e-6)
%{neuron | learning_rate: clamped_learning_rate}
else
neuron
end
end
defp apply_activation(x, :tanh), do: :math.tanh(x)
defp apply_activation(x, :relu), do: max(0.0, x)
defp apply_activation(x, :leaky_relu), do: max(0.01 * x, x)
defp apply_activation(x, :sigmoid), do: 1.0 / (1.0 + :math.exp(-x))
defp apply_activation(x, :swish), do: x / (1.0 + :math.exp(-x))
defp apply_activation(x, :gelu) do
# Gaussian Error Linear Unit
0.5 * x * (1.0 + :math.tanh(:math.sqrt(2.0 / :math.pi()) * (x + 0.044715 * x * x * x)))
end
defp apply_activation(x, _), do: x # Linear activation
defp activation_derivative(y, :tanh), do: 1.0 - y * y
defp activation_derivative(y, :relu), do: if(y > 0, do: 1.0, else: 0.0)
defp activation_derivative(y, :leaky_relu), do: if(y > 0, do: 1.0, else: 0.01)
defp activation_derivative(y, :sigmoid), do: y * (1.0 - y)
defp activation_derivative(y, :swish), do: y + apply_activation(y, :sigmoid) * (1.0 - y)
defp activation_derivative(y, :gelu) do
# Approximate GELU derivative
tanh_term = :math.tanh(:math.sqrt(2.0 / :math.pi()) * (y + 0.044715 * y * y * y))
0.5 * (1.0 + tanh_term) + 0.5 * y * (1.0 - tanh_term * tanh_term) *
:math.sqrt(2.0 / :math.pi()) * (1.0 + 0.134145 * y * y)
end
defp activation_derivative(_y, _), do: 1.0 # Linear activation
defp calculate_avg_magnitude(gradients) do
if map_size(gradients) > 0 do
total = Enum.reduce(gradients, 0.0, fn {_k, v}, acc -> acc + abs(v) end)
total / map_size(gradients)
else
0.0
end
end
end
defmodule AttentionMechanism do
@moduledoc "Multi-head attention mechanism"
defstruct [
:num_heads,
:head_dim,
:query_weights,
:key_weights,
:value_weights,
:output_weights,
:attention_history,
:temperature,
:dropout_rate
]
def new(model_dim, num_heads \\ 8, opts \\ []) do
head_dim = div(model_dim, num_heads)
temperature = Keyword.get(opts, :temperature, 1.0)
dropout_rate = Keyword.get(opts, :dropout_rate, 0.1)
# Initialize weight matrices
query_weights = initialize_weight_matrix(model_dim, model_dim)
key_weights = initialize_weight_matrix(model_dim, model_dim)
value_weights = initialize_weight_matrix(model_dim, model_dim)
output_weights = initialize_weight_matrix(model_dim, model_dim)
%__MODULE__{
num_heads: num_heads,
head_dim: head_dim,
query_weights: query_weights,
key_weights: key_weights,
value_weights: value_weights,
output_weights: output_weights,
attention_history: [],
temperature: temperature,
dropout_rate: dropout_rate
}
end
def forward(%__MODULE__{} = attention, query, key, value, mask \\ nil) do
batch_size = length(query)
_seq_len = if batch_size > 0, do: length(hd(query)), else: 0
# Linear projections
queries = matrix_multiply(query, attention.query_weights)
keys = matrix_multiply(key, attention.key_weights)
values = matrix_multiply(value, attention.value_weights)
# Reshape for multi-head attention
queries_heads = reshape_for_heads(queries, attention.num_heads, attention.head_dim)
keys_heads = reshape_for_heads(keys, attention.num_heads, attention.head_dim)
values_heads = reshape_for_heads(values, attention.num_heads, attention.head_dim)
# Scaled dot-product attention for each head
attention_outputs = Enum.zip([queries_heads, keys_heads, values_heads])
|> Enum.map(fn list -> List.to_tuple(list) end)
|> Enum.map(fn {q, k, v} ->
scaled_dot_product_attention(q, k, v, attention.temperature, mask)
end)
# Concatenate heads
concatenated = concatenate_heads(attention_outputs)
# Final linear projection
output = matrix_multiply(concatenated, attention.output_weights)
# Record attention patterns
attention_weights = extract_attention_weights(attention_outputs)
updated_attention = record_attention_pattern(attention, attention_weights)
{output, updated_attention}
end
defp initialize_weight_matrix(rows, cols) do
# Xavier initialization
limit = :math.sqrt(6.0 / (rows + cols))
for _i <- 1..rows do
for _j <- 1..cols do
(:rand.uniform() * 2 - 1) * limit
end
end
end
defp matrix_multiply(a, b) do
# Simple matrix multiplication
Enum.map(a, fn row ->
Enum.zip(b)
|> Enum.map(fn col_tuple ->
col = Tuple.to_list(col_tuple)
Enum.zip(row, col)
|> Enum.reduce(0.0, fn {x, y}, acc -> acc + x * y end)
end)
end)
end
defp reshape_for_heads(matrix, num_heads, _head_dim) do
# Reshape matrix for multi-head attention
# This is a simplified version
chunk_size = max(div(length(hd(matrix)), num_heads), 1)
Enum.map(1..num_heads, fn head_idx ->
Enum.map(matrix, fn row ->
start_idx = (head_idx - 1) * chunk_size
end_idx = min(head_idx * chunk_size - 1, length(row) - 1)
if start_idx <= end_idx do
Enum.slice(row, start_idx..end_idx)
else
[0.0] # Fallback
end
end)
end)
end
defp scaled_dot_product_attention(query, key, value, temperature, mask) do
# Calculate attention scores
scores = matrix_multiply(query, transpose(key))
# Scale by sqrt(d_k)
scale_factor = 1.0 / :math.sqrt(length(hd(key)))
scaled_scores = Enum.map(scores, fn row ->
Enum.map(row, &(&1 * scale_factor / temperature))
end)
# Apply mask if provided
masked_scores = if mask do
apply_attention_mask(scaled_scores, mask)
else
scaled_scores
end
# Softmax
attention_weights = softmax_matrix(masked_scores)
# Apply attention to values
output = matrix_multiply(attention_weights, value)
{output, attention_weights}
end
defp transpose(matrix) do
if length(matrix) > 0 and length(hd(matrix)) > 0 do
for i <- 0..(length(hd(matrix)) - 1) do
for row <- matrix do
Enum.at(row, i)
end
end
else
[]
end
end
defp apply_attention_mask(scores, mask) do
Enum.zip(scores, mask)
|> Enum.map(fn {score_row, mask_row} ->
Enum.zip(score_row, mask_row)
|> Enum.map(fn {score, mask_val} ->
if mask_val == 0, do: -1.0e9, else: score
end)
end)
end
defp softmax_matrix(matrix) do
Enum.map(matrix, &softmax_row/1)
end
defp softmax_row(row) do
max_val = Enum.max(row)
exp_vals = Enum.map(row, &:math.exp(&1 - max_val))
sum_exp = Enum.sum(exp_vals)
if sum_exp > 0 do
Enum.map(exp_vals, &(&1 / sum_exp))
else
# Uniform distribution if all values are the same
uniform_prob = 1.0 / length(row)
Enum.map(row, fn _ -> uniform_prob end)
end
end
defp concatenate_heads(attention_outputs) do
# Concatenate outputs from all attention heads
Enum.zip(attention_outputs)
|> Enum.map(fn head_outputs ->
head_outputs
|> Tuple.to_list()
|> Enum.flat_map(& &1)
end)
end
defp extract_attention_weights(attention_outputs) do
Enum.map(attention_outputs, fn {_output, weights} -> weights end)
end
defp record_attention_pattern(%__MODULE__{} = attention, attention_weights) do
pattern_record = %{
timestamp: DateTime.utc_now(),
attention_entropy: calculate_attention_entropy(attention_weights),
attention_sparsity: calculate_attention_sparsity(attention_weights),
max_attention: calculate_max_attention(attention_weights)
}
updated_history = [pattern_record | Enum.take(attention.attention_history, 99)]
%{attention | attention_history: updated_history}
end
defp calculate_attention_entropy(attention_weights) do
# Calculate entropy of attention distribution
all_weights = List.flatten(attention_weights)
if length(all_weights) > 0 do
# Normalize weights
total = Enum.sum(all_weights)
if total > 0 do
probs = Enum.map(all_weights, &(&1 / total))
-Enum.reduce(probs, 0.0, fn p, acc ->
if p > 1.0e-10 do
acc + p * :math.log(p)
else
acc
end
end)
else
0.0
end
else
0.0
end
end
defp calculate_attention_sparsity(attention_weights) do
# Calculate sparsity (how focused the attention is)
all_weights = List.flatten(attention_weights)
if length(all_weights) > 0 do
max_weight = Enum.max(all_weights)
avg_weight = Enum.sum(all_weights) / length(all_weights)
if avg_weight > 0 do
max_weight / avg_weight
else
1.0
end
else
1.0
end
end
defp calculate_max_attention(attention_weights) do
if length(attention_weights) > 0 do
List.flatten(attention_weights) |> Enum.max()
else
0.0
end
end
end
defmodule TransformerLayer do
@moduledoc "Transformer layer with self-attention and feed-forward network"
defstruct [
:layer_id,
:self_attention,
:feed_forward,
:layer_norm1,
:layer_norm2,
:residual_connections,
:performance_metrics
]
def new(layer_id, model_dim, num_heads \\ 8, ff_dim \\ nil) do
ff_dim = ff_dim || model_dim * 4
%__MODULE__{
layer_id: layer_id,
self_attention: AttentionMechanism.new(model_dim, num_heads),
feed_forward: create_feed_forward_network(model_dim, ff_dim),
layer_norm1: create_layer_norm(model_dim),
layer_norm2: create_layer_norm(model_dim),
residual_connections: %{enabled: true, scaling: 1.0},
performance_metrics: %{
forward_passes: 0,
attention_entropy: [],
gradient_norms: []
}
}
end
def forward(%__MODULE__{} = layer, input, mask \\ nil) do
# Self-attention with residual connection and layer norm
{attention_output, updated_attention} = AttentionMechanism.forward(
layer.self_attention, input, input, input, mask
)
# Add residual connection and layer norm
norm1_input = add_residual(input, attention_output, layer.residual_connections)
norm1_output = apply_layer_norm(norm1_input, layer.layer_norm1)
# Feed-forward network with residual connection and layer norm
ff_output = forward_feed_forward(norm1_output, layer.feed_forward)
norm2_input = add_residual(norm1_output, ff_output, layer.residual_connections)
norm2_output = apply_layer_norm(norm2_input, layer.layer_norm2)
# Update performance metrics
updated_metrics = update_performance_metrics(layer.performance_metrics, updated_attention)
updated_layer = %{layer |
self_attention: updated_attention,
performance_metrics: updated_metrics
}
{norm2_output, updated_layer}
end
defp create_feed_forward_network(input_dim, hidden_dim) do
%{
linear1: %{
weights: initialize_weight_matrix(input_dim, hidden_dim),
bias: Enum.map(1..hidden_dim, fn _ -> (:rand.uniform() * 2 - 1) * 0.1 end)
},
linear2: %{
weights: initialize_weight_matrix(hidden_dim, input_dim),
bias: Enum.map(1..input_dim, fn _ -> (:rand.uniform() * 2 - 1) * 0.1 end)
},
activation: :gelu,
dropout_rate: 0.1
}
end
defp create_layer_norm(dim) do
%{
gamma: Enum.map(1..dim, fn _ -> 1.0 end),
beta: Enum.map(1..dim, fn _ -> 0.0 end),
epsilon: 1.0e-6
}
end
defp initialize_weight_matrix(rows, cols) do
limit = :math.sqrt(6.0 / (rows + cols))
for _i <- 1..rows do
for _j <- 1..cols do
(:rand.uniform() * 2 - 1) * limit
end
end
end
defp add_residual(input, output, residual_config) do
if residual_config.enabled do
scaling = residual_config.scaling
Enum.zip(input, output)
|> Enum.map(fn {input_row, output_row} ->
Enum.zip(input_row, output_row)
|> Enum.map(fn {inp, out} -> inp + scaling * out end)
end)
else
output
end
end
defp apply_layer_norm(input, layer_norm) do
Enum.zip(input, [layer_norm.gamma, layer_norm.beta])
|> Enum.map(fn {row, [gamma_row, beta_row]} ->
# Calculate mean and variance
mean = Enum.sum(row) / length(row)
variance = Enum.reduce(row, 0.0, fn x, acc ->
diff = x - mean
acc + diff * diff
end) / length(row)
std = :math.sqrt(variance + layer_norm.epsilon)
# Normalize and scale
Enum.zip([row, gamma_row, beta_row])
|> Enum.map(fn list -> List.to_tuple(list) end)
|> Enum.map(fn {x, gamma, beta} ->
normalized = (x - mean) / std
gamma * normalized + beta
end)
end)
end
defp forward_feed_forward(input, ff_network) do
# First linear transformation
hidden = matrix_multiply(input, ff_network.linear1.weights)
|> add_bias(ff_network.linear1.bias)
|> apply_activation_to_matrix(ff_network.activation)
# Apply dropout (simplified - just scale)
dropout_scale = 1.0 - ff_network.dropout_rate
hidden_dropped = Enum.map(hidden, fn row ->
Enum.map(row, &(&1 * dropout_scale))
end)
# Second linear transformation
matrix_multiply(hidden_dropped, ff_network.linear2.weights)
|> add_bias(ff_network.linear2.bias)
end
defp matrix_multiply(a, b) do
Enum.map(a, fn row ->
Enum.zip(b)
|> Enum.map(fn col_tuple ->
col = Tuple.to_list(col_tuple)
Enum.zip(row, col)
|> Enum.reduce(0.0, fn {x, y}, acc -> acc + x * y end)
end)
end)
end
defp add_bias(matrix, bias) do
Enum.map(matrix, fn row ->
Enum.zip(row, bias)
|> Enum.map(fn {x, b} -> x + b end)
end)
end
defp apply_activation_to_matrix(matrix, activation_fn) do
Enum.map(matrix, fn row ->
Enum.map(row, &apply_activation(&1, activation_fn))
end)
end
defp apply_activation(x, :gelu) do
0.5 * x * (1.0 + :math.tanh(:math.sqrt(2.0 / :math.pi()) * (x + 0.044715 * x * x * x)))
end
defp apply_activation(x, :relu), do: max(0.0, x)
defp apply_activation(x, :tanh), do: :math.tanh(x)
defp apply_activation(x, _), do: x
defp update_performance_metrics(metrics, attention) do
new_forward_passes = metrics.forward_passes + 1
# Extract latest attention entropy
latest_entropy = case attention.attention_history do
[latest | _] -> latest.attention_entropy
[] -> 0.0
end
updated_entropy_history = [latest_entropy | Enum.take(metrics.attention_entropy, 99)]
%{metrics |
forward_passes: new_forward_passes,
attention_entropy: updated_entropy_history
}
end
end
defmodule CognitiveTransformer do
@moduledoc "Full transformer model for cognitive reasoning"
defstruct [
:model_id,
:layers,
:embedding_dim,
:num_heads,
:num_layers,
:positional_encoding,
:output_projection,
:training_state,
:evolution_history,
:attention_patterns,
:cognitive_specializations
]
def new(model_id, embedding_dim \\ 512, num_heads \\ 8, num_layers \\ 6) do
layers = Enum.map(1..num_layers, fn i ->
TransformerLayer.new("layer_#{i}", embedding_dim, num_heads)
end)
%__MODULE__{
model_id: model_id,
layers: layers,
embedding_dim: embedding_dim,
num_heads: num_heads,
num_layers: num_layers,
positional_encoding: create_positional_encoding(1000, embedding_dim),
output_projection: create_output_projection(embedding_dim),
training_state: %{
epoch: 0,
total_steps: 0,
loss_history: [],
learning_rate: 0.0001
},
evolution_history: [],
attention_patterns: %{},
cognitive_specializations: initialize_cognitive_specializations()
}
end
def forward(%__MODULE__{} = model, input_embeddings, mask \\ nil) do
# Add positional encoding
pos_encoded = add_positional_encoding(input_embeddings, model.positional_encoding)
# Forward through transformer layers
{layer_outputs, updated_layers} = Enum.reduce(model.layers, {pos_encoded, []},
fn layer, {current_input, acc_layers} ->
{layer_output, updated_layer} = TransformerLayer.forward(layer, current_input, mask)
{layer_output, [updated_layer | acc_layers]}
end)
# Reverse to maintain order
updated_layers = Enum.reverse(updated_layers)
# Apply output projection
final_output = apply_output_projection(layer_outputs, model.output_projection)
# Update attention patterns
updated_attention_patterns = extract_attention_patterns(updated_layers)
# Update model
updated_model = %{model |
layers: updated_layers,
attention_patterns: updated_attention_patterns
}
{final_output, updated_model}
end
def evolve_architecture(%__MODULE__{} = model, evolution_config \\ %{}) do
# Evolve the neural architecture based on performance
mutation_rate = Map.get(evolution_config, :mutation_rate, 0.1)
_selection_pressure = Map.get(evolution_config, :selection_pressure, 0.8)
# Analyze current performance
performance_analysis = analyze_model_performance(model)
# Generate mutations
mutations = generate_architecture_mutations(model, mutation_rate, performance_analysis)
# Apply mutations
mutated_model = apply_architecture_mutations(model, mutations)
# Record evolution
evolution_record = %{
timestamp: DateTime.utc_now(),
mutations_applied: length(mutations),
performance_before: performance_analysis,
mutation_types: Enum.map(mutations, & &1.type)
}
updated_evolution_history = [evolution_record | Enum.take(model.evolution_history, 99)]
%{mutated_model | evolution_history: updated_evolution_history}
end
defp create_positional_encoding(max_length, embedding_dim) do
# Create sinusoidal positional encoding
Enum.map(0..(max_length - 1), fn pos ->
Enum.map(0..(embedding_dim - 1), fn i ->
if rem(i, 2) == 0 do
:math.sin(pos / :math.pow(10000, i / embedding_dim))
else
:math.cos(pos / :math.pow(10000, (i - 1) / embedding_dim))
end
end)
end)
end
defp create_output_projection(embedding_dim) do
%{
weights: initialize_weight_matrix(embedding_dim, embedding_dim),
bias: Enum.map(1..embedding_dim, fn _ -> 0.0 end),
activation: :tanh
}
end
defp initialize_weight_matrix(rows, cols) do
limit = :math.sqrt(6.0 / (rows + cols))
for _i <- 1..rows do
for _j <- 1..cols do
(:rand.uniform() * 2 - 1) * limit
end
end
end
defp add_positional_encoding(embeddings, pos_encoding) do
Enum.zip(embeddings, pos_encoding)
|> Enum.map(fn {emb_row, pos_row} ->
Enum.zip(emb_row, pos_row)
|> Enum.map(fn {emb, pos} -> emb + pos end)
end)
end
defp apply_output_projection(input, projection) do
# Apply final projection layer
result = matrix_multiply(input, projection.weights)
|> add_bias(projection.bias)
# Apply activation if specified
case projection.activation do
nil -> result
activation_fn -> apply_activation_to_matrix(result, activation_fn)
end
end
defp matrix_multiply(a, b) do
Enum.map(a, fn row ->
Enum.zip(b)
|> Enum.map(fn col_tuple ->
col = Tuple.to_list(col_tuple)
Enum.zip(row, col)
|> Enum.reduce(0.0, fn {x, y}, acc -> acc + x * y end)
end)
end)
end
defp add_bias(matrix, bias) do
Enum.map(matrix, fn row ->
Enum.zip(row, bias)
|> Enum.map(fn {x, b} -> x + b end)
end)
end
defp apply_activation_to_matrix(matrix, activation_fn) do
Enum.map(matrix, fn row ->
Enum.map(row, &apply_activation(&1, activation_fn))
end)
end
defp apply_activation(x, :tanh), do: :math.tanh(x)
defp apply_activation(x, :relu), do: max(0.0, x)
defp apply_activation(x, _), do: x
defp extract_attention_patterns(layers) do
# Extract attention patterns from all layers
attention_data = Enum.map(layers, fn layer ->
attention_history = layer.self_attention.attention_history
if length(attention_history) > 0 do
latest = hd(attention_history)
%{
layer_id: layer.layer_id,
entropy: latest.attention_entropy,
sparsity: latest.attention_sparsity,
max_attention: latest.max_attention
}
else
%{
layer_id: layer.layer_id,
entropy: 0.0,
sparsity: 1.0,
max_attention: 0.0
}
end
end)
%{
timestamp: DateTime.utc_now(),
layer_patterns: attention_data,
avg_entropy: calculate_avg_entropy(attention_data),
attention_flow: analyze_attention_flow(attention_data)
}
end
defp calculate_avg_entropy(attention_data) do
if length(attention_data) > 0 do
total_entropy = Enum.reduce(attention_data, 0.0, fn data, acc ->
acc + data.entropy
end)
total_entropy / length(attention_data)
else
0.0
end
end
defp analyze_attention_flow(attention_data) do
# Analyze how attention flows through layers
entropies = Enum.map(attention_data, & &1.entropy)
if length(entropies) > 1 do
# Calculate how attention changes across layers
entropy_diffs = Enum.zip(entropies, tl(entropies))
|> Enum.map(fn {curr, next} -> next - curr end)
%{
entropy_trend: if(Enum.sum(entropy_diffs) > 0, do: :increasing, else: :decreasing),
entropy_volatility: calculate_variance(entropy_diffs),
attention_focusing: Enum.any?(entropy_diffs, &(&1 < -0.1))
}
else
%{
entropy_trend: :stable,
entropy_volatility: 0.0,
attention_focusing: false
}
end
end
defp calculate_variance(values) do
if length(values) > 0 do
mean = Enum.sum(values) / length(values)
variance_sum = Enum.reduce(values, 0.0, fn val, acc ->
diff = val - mean
acc + diff * diff
end)
variance_sum / length(values)
else
0.0
end
end
defp analyze_model_performance(%__MODULE__{} = model) do
# Analyze current model performance
layer_performances = Enum.map(model.layers, fn layer ->
%{
layer_id: layer.layer_id,
forward_passes: layer.performance_metrics.forward_passes,
avg_attention_entropy: calculate_avg_from_list(layer.performance_metrics.attention_entropy),
attention_stability: calculate_stability(layer.performance_metrics.attention_entropy)
}
end)
%{
timestamp: DateTime.utc_now(),
overall_complexity: calculate_model_complexity(model),
attention_efficiency: calculate_attention_efficiency(model),
layer_performances: layer_performances,
evolution_generation: length(model.evolution_history)
}
end
defp calculate_avg_from_list(list) do
if length(list) > 0 do
Enum.sum(list) / length(list)
else
0.0
end
end
defp calculate_stability(values) do
if length(values) > 1 do
variance = calculate_variance(values)
mean = calculate_avg_from_list(values)
if mean > 0 do
1.0 - (variance / (mean * mean)) # Coefficient of variation
else
0.0
end
else
1.0
end
end
defp calculate_model_complexity(%__MODULE__{} = model) do
# Calculate model complexity based on parameters and connections
total_parameters = model.num_layers * model.embedding_dim * model.embedding_dim * 4 # Rough estimate
attention_complexity = model.num_layers * model.num_heads * model.embedding_dim
%{
parameter_count: total_parameters,
attention_complexity: attention_complexity,
depth: model.num_layers,
width: model.embedding_dim
}
end
defp calculate_attention_efficiency(%__MODULE__{} = model) do
# Calculate attention efficiency across all layers
if map_size(model.attention_patterns) > 0 and Map.has_key?(model.attention_patterns, :layer_patterns) do
layer_patterns = model.attention_patterns.layer_patterns
if length(layer_patterns) > 0 do
avg_sparsity = Enum.reduce(layer_patterns, 0.0, fn pattern, acc ->
acc + pattern.sparsity
end) / length(layer_patterns)
avg_max_attention = Enum.reduce(layer_patterns, 0.0, fn pattern, acc ->
acc + pattern.max_attention
end) / length(layer_patterns)
%{
sparsity: avg_sparsity,
focus_strength: avg_max_attention,
efficiency_score: avg_sparsity * avg_max_attention
}
else
%{sparsity: 1.0, focus_strength: 0.0, efficiency_score: 0.0}
end
else
%{sparsity: 1.0, focus_strength: 0.0, efficiency_score: 0.0}
end
end
defp generate_architecture_mutations(%__MODULE__{} = model, mutation_rate, performance_analysis) do
# Generate mutations based on performance analysis
mutations = []
# Attention head mutations
mutations = if :rand.uniform() < mutation_rate do
head_mutation = generate_attention_head_mutation(model, performance_analysis)
[head_mutation | mutations]
else
mutations
end
# Layer mutations
mutations = if :rand.uniform() < mutation_rate do
layer_mutation = generate_layer_mutation(model, performance_analysis)
[layer_mutation | mutations]
else
mutations
end
# Activation function mutations
mutations = if :rand.uniform() < mutation_rate do
activation_mutation = generate_activation_mutation(model, performance_analysis)
[activation_mutation | mutations]
else
mutations
end
# Residual connection mutations
mutations = if :rand.uniform() < mutation_rate do
residual_mutation = generate_residual_mutation(model, performance_analysis)
[residual_mutation | mutations]
else
mutations
end
mutations
end
defp generate_attention_head_mutation(%__MODULE__{} = model, performance_analysis) do
# Generate mutation for attention heads
efficiency = performance_analysis.attention_efficiency
mutation_type = cond do
efficiency.efficiency_score < 0.3 -> :increase_heads
efficiency.efficiency_score > 0.8 -> :decrease_heads
true -> :modify_head_dim
end
%{
type: :attention_heads,
mutation_type: mutation_type,
current_heads: model.num_heads,
target_heads: case mutation_type do
:increase_heads -> min(model.num_heads + 2, 16)
:decrease_heads -> max(model.num_heads - 2, 2)
:modify_head_dim -> model.num_heads
end
}
end
defp generate_layer_mutation(%__MODULE__{} = model, performance_analysis) do
# Generate mutation for layer structure
complexity = performance_analysis.overall_complexity
mutation_type = cond do
complexity.depth < 4 -> :add_layer
complexity.depth > 12 -> :remove_layer
true -> :modify_layer_norm
end
%{
type: :layer_structure,
mutation_type: mutation_type,
current_layers: model.num_layers,
target_layers: case mutation_type do
:add_layer -> model.num_layers + 1
:remove_layer -> max(model.num_layers - 1, 2)
:modify_layer_norm -> model.num_layers
end
}
end
defp generate_activation_mutation(%__MODULE__{} = _model, _performance_analysis) do
# Generate mutation for activation functions
activations = [:gelu, :relu, :swish, :tanh, :leaky_relu]
new_activation = Enum.random(activations)
%{
type: :activation_function,
mutation_type: :change_activation,
new_activation: new_activation
}
end
defp generate_residual_mutation(%__MODULE__{} = _model, performance_analysis) do
# Generate mutation for residual connections
efficiency = performance_analysis.attention_efficiency
scaling_factor = if efficiency.efficiency_score > 0.7 do
1.1 # Increase residual scaling
else
0.9 # Decrease residual scaling
end
%{
type: :residual_connections,
mutation_type: :adjust_scaling,
scaling_factor: scaling_factor
}
end
defp apply_architecture_mutations(%__MODULE__{} = model, mutations) do
# Apply mutations to model architecture
Enum.reduce(mutations, model, fn mutation, acc_model ->
apply_single_mutation(acc_model, mutation)
end)
end
defp apply_single_mutation(%__MODULE__{} = model, mutation) do
case mutation.type do
:attention_heads ->
apply_attention_head_mutation(model, mutation)
:layer_structure ->
apply_layer_structure_mutation(model, mutation)
:activation_function ->
apply_activation_function_mutation(model, mutation)
:residual_connections ->
apply_residual_connection_mutation(model, mutation)
_ ->
model
end
end
defp apply_attention_head_mutation(%__MODULE__{} = model, mutation) do
case mutation.mutation_type do
:increase_heads ->
new_layers = Enum.map(model.layers, fn layer ->
new_attention = AttentionMechanism.new(model.embedding_dim, mutation.target_heads)
%{layer | self_attention: new_attention}
end)
%{model |
layers: new_layers,
num_heads: mutation.target_heads
}
:decrease_heads ->
new_layers = Enum.map(model.layers, fn layer ->
new_attention = AttentionMechanism.new(model.embedding_dim, mutation.target_heads)
%{layer | self_attention: new_attention}
end)
%{model |
layers: new_layers,
num_heads: mutation.target_heads
}
_ ->
model
end
end
defp apply_layer_structure_mutation(%__MODULE__{} = model, mutation) do
case mutation.mutation_type do
:add_layer ->
new_layer = TransformerLayer.new("layer_#{model.num_layers + 1}",
model.embedding_dim, model.num_heads)
new_layers = model.layers ++ [new_layer]
%{model |
layers: new_layers,
num_layers: mutation.target_layers
}
:remove_layer ->
# Remove the layer with the worst performance
worst_layer_idx = find_worst_performing_layer(model)
new_layers = List.delete_at(model.layers, worst_layer_idx)
%{model |
layers: new_layers,
num_layers: mutation.target_layers
}
_ ->
model
end
end
defp apply_activation_function_mutation(%__MODULE__{} = model, mutation) do
# Apply activation function mutation to feed-forward networks
new_layers = Enum.map(model.layers, fn layer ->
updated_ff = %{layer.feed_forward | activation: mutation.new_activation}
%{layer | feed_forward: updated_ff}
end)
%{model | layers: new_layers}
end
defp apply_residual_connection_mutation(%__MODULE__{} = model, mutation) do
# Apply residual connection mutation
new_layers = Enum.map(model.layers, fn layer ->
updated_residual = %{layer.residual_connections | scaling: mutation.scaling_factor}
%{layer | residual_connections: updated_residual}
end)
%{model | layers: new_layers}
end
defp find_worst_performing_layer(%__MODULE__{} = model) do
# Find the layer with the worst performance (lowest attention entropy)
layer_scores = Enum.with_index(model.layers)
|> Enum.map(fn {layer, idx} ->
avg_entropy = calculate_avg_from_list(layer.performance_metrics.attention_entropy)
{idx, avg_entropy}
end)
if length(layer_scores) > 0 do
{worst_idx, _score} = Enum.min_by(layer_scores, fn {_idx, score} -> score end)
worst_idx
else
0
end
end
defp initialize_cognitive_specializations do
# Initialize cognitive specializations for different types of reasoning
%{
logical_reasoning: %{
preferred_activations: [:relu, :leaky_relu],
attention_pattern: :focused,
layer_emphasis: :deep
},
creative_thinking: %{
preferred_activations: [:gelu, :swish],
attention_pattern: :distributed,
layer_emphasis: :wide
},
analytical_processing: %{
preferred_activations: [:tanh, :relu],
attention_pattern: :hierarchical,
layer_emphasis: :balanced
},
empathetic_understanding: %{
preferred_activations: [:gelu, :swish],
attention_pattern: :contextual,
layer_emphasis: :shallow
}
}
end
end
# Main GenServer implementation
def start_link(opts \\ []) do
name = Keyword.get(opts, :name, __MODULE__)
embedding_dim = Keyword.get(opts, :embedding_dim, 512)
num_heads = Keyword.get(opts, :num_heads, 8)
num_layers = Keyword.get(opts, :num_layers, 6)
GenServer.start_link(__MODULE__, {embedding_dim, num_heads, num_layers}, name: name)
end
def create_model(neural_pid, model_id, config \\ %{}) do
GenServer.call(neural_pid, {:create_model, model_id, config})
end
def forward_pass(neural_pid, model_id, input_embeddings, mask \\ nil) do
GenServer.call(neural_pid, {:forward_pass, model_id, input_embeddings, mask})
end
def evolve_model(neural_pid, model_id, evolution_config \\ %{}) do
GenServer.call(neural_pid, {:evolve_model, model_id, evolution_config})
end
def get_model_state(neural_pid, model_id) do
GenServer.call(neural_pid, {:get_model_state, model_id})
end
def analyze_attention_patterns(neural_pid, model_id) do
GenServer.call(neural_pid, {:analyze_attention_patterns, model_id})
end
def train_step(neural_pid, model_id, training_data) do
GenServer.call(neural_pid, {:train_step, model_id, training_data})
end
# Server callbacks
@impl true
def init({embedding_dim, num_heads, num_layers}) do
state = %{
models: %{},
default_config: %{
embedding_dim: embedding_dim,
num_heads: num_heads,
num_layers: num_layers
},
evolution_log: [],
performance_metrics: %{},
neural_plasticity: %{
adaptation_rate: 0.01,
synaptic_strength: 1.0,
hebbian_learning: true
}
}
Logger.info("Neural Architecture system initialized with #{num_layers} layers, #{num_heads} heads, #{embedding_dim}D embeddings")
{:ok, state}
end
@impl true
def handle_call({:create_model, model_id, config}, _from, state) do
# Merge config with defaults
final_config = Map.merge(state.default_config, config)
# Create new transformer model
transformer = CognitiveTransformer.new(
model_id,
final_config.embedding_dim,
final_config.num_heads,
final_config.num_layers
)
# Add to models
updated_models = Map.put(state.models, model_id, transformer)
# Initialize performance metrics
updated_metrics = Map.put(state.performance_metrics, model_id, %{
creation_time: DateTime.utc_now(),
forward_passes: 0,
evolution_count: 0,
performance_scores: []
})
new_state = %{state |
models: updated_models,
performance_metrics: updated_metrics
}
Logger.info("Created cognitive transformer model: #{model_id}")
{:reply, {:ok, model_id}, new_state}
end
@impl true
def handle_call({:forward_pass, model_id, input_embeddings, mask}, _from, state) do
case Map.get(state.models, model_id) do
nil ->
{:reply, {:error, :model_not_found}, state}
model ->
# Perform forward pass
{output, updated_model} = CognitiveTransformer.forward(model, input_embeddings, mask)
# Update model in state
updated_models = Map.put(state.models, model_id, updated_model)
# Update performance metrics
updated_metrics = update_model_metrics(state.performance_metrics, model_id, :forward_pass)
new_state = %{state |
models: updated_models,
performance_metrics: updated_metrics
}
result = %{
output: output,
attention_patterns: updated_model.attention_patterns,
model_stats: extract_model_stats(updated_model)
}
{:reply, {:ok, result}, new_state}
end
end
@impl true
def handle_call({:evolve_model, model_id, evolution_config}, _from, state) do
case Map.get(state.models, model_id) do
nil ->
{:reply, {:error, :model_not_found}, state}
model ->
# Evolve the model architecture
evolved_model = CognitiveTransformer.evolve_architecture(model, evolution_config)
# Update model in state
updated_models = Map.put(state.models, model_id, evolved_model)
# Log evolution
evolution_entry = %{
timestamp: DateTime.utc_now(),
model_id: model_id,
evolution_config: evolution_config,
mutations_applied: length(hd(evolved_model.evolution_history).mutations_applied)
}
updated_evolution_log = [evolution_entry | Enum.take(state.evolution_log, 99)]
# Update performance metrics
updated_metrics = update_model_metrics(state.performance_metrics, model_id, :evolution)
new_state = %{state |
models: updated_models,
evolution_log: updated_evolution_log,
performance_metrics: updated_metrics
}
Logger.info("Evolved model #{model_id}: #{evolution_entry.mutations_applied} mutations applied")
result = %{
evolution_successful: true,
mutations_applied: evolution_entry.mutations_applied,
model_generation: length(evolved_model.evolution_history),
new_architecture_stats: extract_architecture_stats(evolved_model)
}
{:reply, {:ok, result}, new_state}
end
end
@impl true
def handle_call({:get_model_state, model_id}, _from, state) do
case Map.get(state.models, model_id) do
nil ->
{:reply, {:error, :model_not_found}, state}
model ->
model_state = %{
model_id: model.model_id,
architecture: %{
embedding_dim: model.embedding_dim,
num_heads: model.num_heads,
num_layers: model.num_layers
},
training_state: model.training_state,
evolution_generation: length(model.evolution_history),
attention_patterns: model.attention_patterns,
cognitive_specializations: model.cognitive_specializations,
performance_metrics: Map.get(state.performance_metrics, model_id, %{})
}
{:reply, model_state, state}
end
end
@impl true
def handle_call({:analyze_attention_patterns, model_id}, _from, state) do
case Map.get(state.models, model_id) do
nil ->
{:reply, {:error, :model_not_found}, state}
model ->
# Analyze attention patterns across layers
layer_analyses = Enum.map(model.layers, fn layer ->
attention_history = layer.self_attention.attention_history
%{
layer_id: layer.layer_id,
recent_entropy: if(length(attention_history) > 0, do: hd(attention_history).attention_entropy, else: 0.0),
entropy_trend: calculate_entropy_trend(attention_history),
attention_focus: analyze_attention_focus(attention_history),
performance_score: calculate_layer_performance_score(layer)
}
end)
# Global analysis
global_analysis = %{
model_id: model_id,
overall_attention_health: calculate_overall_attention_health(layer_analyses),
attention_flow_pattern: analyze_cross_layer_attention_flow(layer_analyses),
cognitive_mode_detection: detect_cognitive_modes(layer_analyses),
optimization_suggestions: generate_optimization_suggestions(layer_analyses)
}
result = %{
layer_analyses: layer_analyses,
global_analysis: global_analysis,
attention_visualization_data: prepare_attention_visualization_data(model)
}
{:reply, {:ok, result}, state}
end
end
@impl true
def handle_call({:train_step, model_id, training_data}, _from, state) do
case Map.get(state.models, model_id) do
nil ->
{:reply, {:error, :model_not_found}, state}
model ->
# Simplified training step (would normally involve gradient computation)
{_output, updated_model} = CognitiveTransformer.forward(model, training_data.inputs, training_data.mask)
# Update training state
updated_training_state = %{
updated_model.training_state |
total_steps: updated_model.training_state.total_steps + 1,
epoch: training_data.epoch || updated_model.training_state.epoch
}
final_model = %{updated_model | training_state: updated_training_state}
# Update models
updated_models = Map.put(state.models, model_id, final_model)
# Update metrics
updated_metrics = update_model_metrics(state.performance_metrics, model_id, :training_step)
new_state = %{state |
models: updated_models,
performance_metrics: updated_metrics
}
result = %{
training_step_completed: true,
step: final_model.training_state.total_steps,
epoch: final_model.training_state.epoch
}
{:reply, {:ok, result}, new_state}
end
end
# Helper functions
defp extract_model_stats(%CognitiveTransformer{} = model) do
%{
total_parameters: estimate_parameter_count(model),
attention_efficiency: calculate_current_attention_efficiency(model),
layer_count: model.num_layers,
head_count: model.num_heads,
embedding_dimension: model.embedding_dim,
evolution_generation: length(model.evolution_history)
}
end
defp extract_architecture_stats(%CognitiveTransformer{} = model) do
%{
architecture_complexity: calculate_architecture_complexity(model),
specialization_scores: calculate_specialization_scores(model),
attention_distribution: analyze_attention_distribution(model),
layer_efficiency: calculate_layer_efficiency_scores(model)
}
end
defp estimate_parameter_count(%CognitiveTransformer{} = model) do
# Rough parameter count estimation
embedding_params = model.embedding_dim * model.embedding_dim
attention_params = model.num_layers * model.num_heads * model.embedding_dim * model.embedding_dim * 4
ff_params = model.num_layers * model.embedding_dim * model.embedding_dim * 8
embedding_params + attention_params + ff_params
end
defp calculate_current_attention_efficiency(%CognitiveTransformer{} = model) do
if map_size(model.attention_patterns) > 0 and Map.has_key?(model.attention_patterns, :layer_patterns) do
layer_patterns = model.attention_patterns.layer_patterns
if length(layer_patterns) > 0 do
total_efficiency = Enum.reduce(layer_patterns, 0.0, fn pattern, acc ->
efficiency = pattern.sparsity * pattern.max_attention
acc + efficiency
end)
total_efficiency / length(layer_patterns)
else
0.0
end
else
0.0
end
end
defp calculate_architecture_complexity(%CognitiveTransformer{} = model) do
# Calculate architectural complexity score
depth_complexity = model.num_layers / 12.0 # Normalized to typical max
width_complexity = model.embedding_dim / 1024.0 # Normalized to typical max
attention_complexity = model.num_heads / 16.0 # Normalized to typical max
(depth_complexity + width_complexity + attention_complexity) / 3
end
defp calculate_specialization_scores(%CognitiveTransformer{} = model) do
# Calculate how specialized the model is for different cognitive tasks
specializations = model.cognitive_specializations
Map.new(specializations, fn {specialization, config} ->
# Score based on how well the current architecture matches the specialization
score = calculate_specialization_match_score(model, config)
{specialization, score}
end)
end
defp calculate_specialization_match_score(%CognitiveTransformer{} = _model, _specialization_config) do
# Simplified specialization matching
:rand.uniform() * 0.5 + 0.25 # Random score between 0.25 and 0.75
end
defp analyze_attention_distribution(%CognitiveTransformer{} = model) do
if map_size(model.attention_patterns) > 0 and Map.has_key?(model.attention_patterns, :layer_patterns) do
layer_patterns = model.attention_patterns.layer_patterns
entropies = Enum.map(layer_patterns, & &1.entropy)
sparsities = Enum.map(layer_patterns, & &1.sparsity)
%{
entropy_distribution: %{
mean: calculate_mean(entropies),
variance: calculate_variance(entropies),
min: if(length(entropies) > 0, do: Enum.min(entropies), else: 0.0),
max: if(length(entropies) > 0, do: Enum.max(entropies), else: 0.0)
},
sparsity_distribution: %{
mean: calculate_mean(sparsities),
variance: calculate_variance(sparsities),
min: if(length(sparsities) > 0, do: Enum.min(sparsities), else: 0.0),
max: if(length(sparsities) > 0, do: Enum.max(sparsities), else: 0.0)
}
}
else
%{
entropy_distribution: %{mean: 0.0, variance: 0.0, min: 0.0, max: 0.0},
sparsity_distribution: %{mean: 1.0, variance: 0.0, min: 1.0, max: 1.0}
}
end
end
defp calculate_mean(values) do
if length(values) > 0 do
Enum.sum(values) / length(values)
else
0.0
end
end
defp calculate_variance(values) do
if length(values) > 1 do
mean = calculate_mean(values)
variance_sum = Enum.reduce(values, 0.0, fn val, acc ->
diff = val - mean
acc + diff * diff
end)
variance_sum / (length(values) - 1)
else
0.0
end
end
defp calculate_layer_efficiency_scores(%CognitiveTransformer{} = model) do
Enum.map(model.layers, fn layer ->
attention_entropy_score = if length(layer.performance_metrics.attention_entropy) > 0 do
avg_entropy = Enum.sum(layer.performance_metrics.attention_entropy) / length(layer.performance_metrics.attention_entropy)
1.0 - avg_entropy # Higher entropy = lower efficiency
else
0.5
end
forward_pass_score = min(layer.performance_metrics.forward_passes / 1000.0, 1.0)
%{
layer_id: layer.layer_id,
attention_efficiency: attention_entropy_score,
usage_efficiency: forward_pass_score,
overall_efficiency: (attention_entropy_score + forward_pass_score) / 2
}
end)
end
defp update_model_metrics(metrics, model_id, operation) do
current_metrics = Map.get(metrics, model_id, %{
creation_time: DateTime.utc_now(),
forward_passes: 0,
evolution_count: 0,
performance_scores: []
})
updated_metrics = case operation do
:forward_pass ->
%{current_metrics | forward_passes: current_metrics.forward_passes + 1}
:evolution ->
%{current_metrics | evolution_count: current_metrics.evolution_count + 1}
:training_step ->
%{current_metrics | forward_passes: current_metrics.forward_passes + 1}
_ ->
current_metrics
end
Map.put(metrics, model_id, updated_metrics)
end
defp calculate_entropy_trend(attention_history) do
if length(attention_history) >= 3 do
recent_entropies = Enum.take(attention_history, 3)
|> Enum.map(& &1.attention_entropy)
|> Enum.reverse() # Oldest to newest
[oldest, middle, newest] = recent_entropies
trend1 = middle - oldest
trend2 = newest - middle
avg_trend = (trend1 + trend2) / 2
cond do
avg_trend > 0.05 -> :increasing
avg_trend < -0.05 -> :decreasing
true -> :stable
end
else
:insufficient_data
end
end
defp analyze_attention_focus(attention_history) do
if length(attention_history) > 0 do
recent_data = hd(attention_history)
%{
focus_strength: recent_data.attention_sparsity,
focus_consistency: calculate_focus_consistency(attention_history),
peak_attention: recent_data.max_attention
}
else
%{
focus_strength: 1.0,
focus_consistency: 0.0,
peak_attention: 0.0
}
end
end
defp calculate_focus_consistency(attention_history) do
if length(attention_history) >= 2 do
sparsities = Enum.take(attention_history, 5)
|> Enum.map(& &1.attention_sparsity)
variance = calculate_variance(sparsities)
1.0 - min(variance, 1.0) # Lower variance = higher consistency
else
0.0
end
end
defp calculate_layer_performance_score(layer) do
attention_score = if length(layer.performance_metrics.attention_entropy) > 0 do
avg_entropy = Enum.sum(layer.performance_metrics.attention_entropy) / length(layer.performance_metrics.attention_entropy)
1.0 - avg_entropy # Lower entropy = better focus = higher score
else
0.5
end
usage_score = min(layer.performance_metrics.forward_passes / 100.0, 1.0)
(attention_score + usage_score) / 2
end
defp calculate_overall_attention_health(layer_analyses) do
if length(layer_analyses) > 0 do
total_performance = Enum.reduce(layer_analyses, 0.0, fn analysis, acc ->
acc + analysis.performance_score
end)
avg_performance = total_performance / length(layer_analyses)
cond do
avg_performance > 0.8 -> :excellent
avg_performance > 0.6 -> :good
avg_performance > 0.4 -> :fair
true -> :poor
end
else
:unknown
end
end
defp analyze_cross_layer_attention_flow(layer_analyses) do
if length(layer_analyses) > 1 do
entropies = Enum.map(layer_analyses, & &1.recent_entropy)
# Analyze flow from input to output layers
flow_direction = if List.last(entropies) > hd(entropies) do
:diverging # Attention spreads out
else
:converging # Attention focuses
end
flow_smoothness = 1.0 - calculate_variance(entropies)
%{
direction: flow_direction,
smoothness: flow_smoothness,
overall_pattern: if(flow_smoothness > 0.7, do: :coherent, else: :chaotic)
}
else
%{
direction: :unknown,
smoothness: 0.0,
overall_pattern: :insufficient_data
}
end
end
defp detect_cognitive_modes(layer_analyses) do
# Detect what type of cognitive processing is happening
avg_entropy = calculate_mean(Enum.map(layer_analyses, & &1.recent_entropy))
avg_focus = calculate_mean(Enum.map(layer_analyses, &(&1.attention_focus.focus_strength)))
detected_mode = cond do
avg_entropy < 0.3 and avg_focus > 0.8 ->
:focused_analytical
avg_entropy > 0.7 and avg_focus < 0.4 ->
:broad_creative
avg_entropy > 0.5 and avg_focus > 0.6 ->
:selective_attention
avg_entropy < 0.5 and avg_focus < 0.5 ->
:systematic_processing
true ->
:balanced_processing
end
%{
primary_mode: detected_mode,
confidence: calculate_mode_detection_confidence(avg_entropy, avg_focus),
characteristics: describe_cognitive_mode(detected_mode)
}
end
defp calculate_mode_detection_confidence(entropy, focus) do
# Higher confidence when values are more extreme
entropy_extremeness = abs(entropy - 0.5) * 2
focus_extremeness = abs(focus - 0.5) * 2
(entropy_extremeness + focus_extremeness) / 2
end
defp describe_cognitive_mode(mode) do
case mode do
:focused_analytical ->
"High focus, low entropy - systematic analytical reasoning"
:broad_creative ->
"Low focus, high entropy - divergent creative thinking"
:selective_attention ->
"High focus, high entropy - selective information processing"
:systematic_processing ->
"Low focus, low entropy - methodical step-by-step reasoning"
:balanced_processing ->
"Moderate focus and entropy - balanced cognitive processing"
end
end
defp generate_optimization_suggestions(layer_analyses) do
suggestions = []
# Check for attention problems
suggestions = if Enum.any?(layer_analyses, &(&1.recent_entropy > 0.9)) do
["Consider reducing model complexity - some layers show very high entropy" | suggestions]
else
suggestions
end
suggestions = if Enum.any?(layer_analyses, &(&1.recent_entropy < 0.1)) do
["Consider increasing model capacity - some layers show very low entropy" | suggestions]
else
suggestions
end
# Check for performance issues
poor_performers = Enum.filter(layer_analyses, &(&1.performance_score < 0.3))
suggestions = if length(poor_performers) > 0 do
layer_ids = Enum.map(poor_performers, & &1.layer_id)
["Consider evolution for layers: #{Enum.join(layer_ids, ", ")}" | suggestions]
else
suggestions
end
# Check for attention flow issues
entropy_trend_issues = Enum.filter(layer_analyses, &(&1.entropy_trend == :insufficient_data))
suggestions = if length(entropy_trend_issues) > length(layer_analyses) / 2 do
["Model needs more training - insufficient attention pattern data" | suggestions]
else
suggestions
end
if length(suggestions) == 0 do
["Model appears to be performing well - no critical issues detected"]
else
suggestions
end
end
defp prepare_attention_visualization_data(%CognitiveTransformer{} = model) do
# Prepare data for attention pattern visualization
if map_size(model.attention_patterns) > 0 and Map.has_key?(model.attention_patterns, :layer_patterns) do
layer_data = Enum.map(model.attention_patterns.layer_patterns, fn pattern ->
%{
layer_id: pattern.layer_id,
entropy: pattern.entropy,
sparsity: pattern.sparsity,
max_attention: pattern.max_attention,
visualization_coords: generate_visualization_coordinates(pattern)
}
end)
%{
layers: layer_data,
global_flow: model.attention_patterns.attention_flow,
timestamp: model.attention_patterns.timestamp
}
else
%{
layers: [],
global_flow: %{},
timestamp: DateTime.utc_now()
}
end
end
defp generate_visualization_coordinates(pattern) do
# Generate 2D coordinates for visualization based on attention properties
%{
x: pattern.entropy, # X-axis represents entropy
y: pattern.sparsity, # Y-axis represents sparsity
size: pattern.max_attention * 10, # Size represents max attention
color_intensity: (pattern.entropy + pattern.sparsity) / 2
}
end
end