You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
526 lines
16 KiB
Plaintext
526 lines
16 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 1,
|
|
"id": "weird-cloud",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"import torch\n",
|
|
"from torch.autograd.functional import jacobian\n",
|
|
"import itertools"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "ready-attention",
|
|
"metadata": {},
|
|
"source": [
|
|
"# Setup Functions\n",
|
|
"## General CompositionFunctions"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 2,
|
|
"id": "convertible-thinking",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"### Set up functions to compose functions \n",
|
|
"# These functions will \n",
|
|
"# - compose two functions together\n",
|
|
"# - compose a function to itself n times.\n",
|
|
"\n",
|
|
"def compose(f,g):\n",
|
|
" \"\"\"\n",
|
|
" this function composes two functions f and g in\n",
|
|
" the order f(g(*args))\n",
|
|
" \n",
|
|
" it returns a function\n",
|
|
" \"\"\"\n",
|
|
" return lambda *args: f(g(*args))\n",
|
|
"\n",
|
|
"def compose_recursive_functions(fn,n):\n",
|
|
" \"\"\"\n",
|
|
" This function takes a function fn and composes it with itself n times\n",
|
|
" \n",
|
|
" Returns an array/list that contains each of the n composition levels, ordered\n",
|
|
" from fn() to fn^n()\n",
|
|
" \"\"\"\n",
|
|
"\n",
|
|
" \n",
|
|
" #Set base conditions\n",
|
|
" out_func = None\n",
|
|
" out_func_list =[]\n",
|
|
"\n",
|
|
" #build the compositions of functions\n",
|
|
" for f in itertools.repeat(fn, n):\n",
|
|
" #if first iteration\n",
|
|
" if out_func == None:\n",
|
|
" out_func = f\n",
|
|
" else:\n",
|
|
" out_func = compose(f,out_func)\n",
|
|
"\n",
|
|
" #append the ou\n",
|
|
" out_func_list.append(out_func)\n",
|
|
" \n",
|
|
" return out_func_list"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "cloudy-creek",
|
|
"metadata": {},
|
|
"source": [
|
|
"# functions related to transitions"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 3,
|
|
"id": "musical-virtue",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"def single_transition(item_to_iterate, laws_motion_fn, profit_fn, stocks, debris, launch_fn):\n",
|
|
" \"\"\"\n",
|
|
" This function represents the inverted envelope conditions.\n",
|
|
" It allows us to describe the derivatives of the value function evaluated at time $t+1$ in terms based in time period $t$.\n",
|
|
" \n",
|
|
" It takes a few derivatives (jacobians), and due to the nature of the return as tuples\n",
|
|
" they must be concatenated.\n",
|
|
" \n",
|
|
" It returns the transitioned values\n",
|
|
" \"\"\"\n",
|
|
" \n",
|
|
" #Find the matrix to invert. \n",
|
|
" #it consists of the derivative of the laws of motion with respect to stocks and debris\n",
|
|
" \n",
|
|
" #Get the jacobian\n",
|
|
" a = jacobian(laws_motion_fn, (stocks,debris, launch_fn(stocks,debris)))\n",
|
|
" \n",
|
|
" #Reassemble the Jacobian nested tuples into the appropriate tensor\n",
|
|
" A = BETA * torch.cat((torch.cat((a[0][0],a[0][1]),dim=1),torch.cat((a[1][0],a[1][1]),dim=1)), dim=0)\n",
|
|
"\n",
|
|
" #TODO: figure out some diagnostics for this section\n",
|
|
" #Possibilities include:\n",
|
|
" # - Det(A) ~= 0\n",
|
|
" # - EigVal(A) ~= 0\n",
|
|
" # - A.inverse() with a try catch system to record types of returns\n",
|
|
" #Alternatively, \n",
|
|
" #if abs(a.det())\n",
|
|
" \n",
|
|
" #Calculate the item to transition\n",
|
|
" f_jacobians = jacobian(profit_fn,(stocks, debris, launch_fn(stocks,debris)))\n",
|
|
"\n",
|
|
" #issue with shape here: my launch function is for all launches, not just a single launch.\n",
|
|
" f_theta = torch.cat([f_jacobians[0][0], f_jacobians[1][0]],axis=0) \n",
|
|
"\n",
|
|
" T = item_to_iterate - f_theta\n",
|
|
"\n",
|
|
" #Includes rearranging the jacobian of profit.\n",
|
|
"\n",
|
|
" #Return the transitioned values\n",
|
|
" return ( A.inverse()/BETA ) @ T\n",
|
|
"\n",
|
|
"# This function wraps the single transition and handles updating dates etc.\n",
|
|
"def transition_wrapper(data_in):\n",
|
|
" \"\"\"\n",
|
|
" \"\"\"\n",
|
|
" #unpack states and functions\n",
|
|
" stocks, debris,profit_fn, laws_motion_fn, item_to_transition,launch_fn = data_in\n",
|
|
" \n",
|
|
" #Calculate new states\n",
|
|
" new_stocks, new_debris = laws_motion_fn(stocks,debris, launch_fn(stocks,debris))\n",
|
|
" \n",
|
|
" #WARNING: RECURSION: You may break your head...\n",
|
|
" #This gets the transition of the value function derivatives over time.\n",
|
|
" transitioned = single_transition(\n",
|
|
" item_to_transition, #item to iterate, i.e. the derivatives of the value function\n",
|
|
" #functions\n",
|
|
" laws_motion_fn, \n",
|
|
" profit_fn, \n",
|
|
" stocks, debris, #states\n",
|
|
" launch_fn #launch function\n",
|
|
" )\n",
|
|
" \n",
|
|
" #collects the data back together for return, including the updated state variables\n",
|
|
" data_out = new_stocks, new_debris, profit_fn, laws_motion_fn, transitioned, launch_fn\n",
|
|
" \n",
|
|
" return data_out"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "quick-destruction",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Setup functions related to the problem"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 4,
|
|
"id": "strategic-classics",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
" \n",
|
|
"\n",
|
|
"### functions\n",
|
|
"\n",
|
|
"def survival(stock, debris):\n",
|
|
" \"\"\"\n",
|
|
" This is a basic, deterministic survival function. \n",
|
|
" It is based on the CDF of an exponential distribution.\n",
|
|
" \"\"\"\n",
|
|
" #SURVIVAL FUNCTION BASED ON AN EXPONENTIAL DISTRIBUTION\n",
|
|
" return 1 - torch.exp(-SCALING * stock - debris)\n",
|
|
"\n",
|
|
"def test_launch(stock, debris):\n",
|
|
" \"\"\"\n",
|
|
" Temporary launch function \n",
|
|
" \"\"\"\n",
|
|
" return torch.ones(5, requires_grad=True),\n",
|
|
"\n",
|
|
"def laws_of_motion(stock, debris, launch):\n",
|
|
" \"\"\"\n",
|
|
" This function updates state variables (stock and debris), according \n",
|
|
" to the laws of motion.\n",
|
|
" \n",
|
|
" It returns the state variables as \n",
|
|
" \"\"\"\n",
|
|
"\n",
|
|
" s = survival(stock,debris)\n",
|
|
" #Notes: Survival is a global function.\n",
|
|
" \n",
|
|
" new_stock = stock*s + launch\n",
|
|
" \n",
|
|
" \n",
|
|
" #TODO: Currently Ignoring autocatalysis\n",
|
|
" new_debris = (1-DELTA)*debris + LAUNCH_DEBRIS_RATE * launch.sum() + COLLISION_DEBRIS_RATE*(1-s) @ stock\n",
|
|
" \n",
|
|
" return (new_stock, new_debris)\n",
|
|
"\n",
|
|
"#This is not a good specification of the profit function, but it will work for now.\n",
|
|
"def profit(stock, debris, launches):\n",
|
|
" return UTIL_WEIGHTS @ stock - LAUNCH_COST*launches\n",
|
|
"\n",
|
|
"\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "hairy-finding",
|
|
"metadata": {},
|
|
"source": [
|
|
"# Actual calculations"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 5,
|
|
"id": "renewable-handy",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"#number of states\n",
|
|
"N = 5\n",
|
|
"\n",
|
|
"#set states\n",
|
|
"stocks = torch.ones(N)\n",
|
|
"#Last one is different if there are more than 1 (just for variety, no theoretical reason)\n",
|
|
"if N > 1:\n",
|
|
" stocks[-1] = 0.5\n",
|
|
"#now add the tracking requirement in place\n",
|
|
"stocks.requires_grad_()\n",
|
|
"\n",
|
|
"#Setup Debris\n",
|
|
"debris = torch.tensor([2.2],requires_grad=True)\n",
|
|
"\n",
|
|
"#CHANGE LATER: Launch is currently a value, should be a function (i.e. neural network)\n",
|
|
"launches = test_launch\n",
|
|
"\n",
|
|
"#Starting point\n",
|
|
"# Stocks, debris, profit fn, laws of motion, item to transition, Launch function\n",
|
|
"base_data = (stocks,debris, profit, laws_of_motion, torch.ones(N+1, requires_grad=True),launches)\n",
|
|
"\n",
|
|
"#Parameters\n",
|
|
"SCALING = torch.ones(5)\n",
|
|
"DELTA = 0.9\n",
|
|
"LAUNCH_DEBRIS_RATE = 0.005\n",
|
|
"LAUNCH_COST = 1.0\n",
|
|
"COLLISION_DEBRIS_RATE = 0.0007\n",
|
|
"UTIL_WEIGHTS = torch.tensor([1,-0.2,0,0,0])\n",
|
|
"BETA = 0.95"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 32,
|
|
"id": "engaged-acceptance",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"(tensor([1.9592, 1.9592, 1.9592, 1.9592, 1.4664], grad_fn=<AddBackward0>), tensor([0.2451], grad_fn=<AddBackward0>), <function profit at 0x7fa6269e4550>, <function laws_of_motion at 0x7fa6269e44c0>, tensor([-0.4523, 0.8774, 0.6558, 0.6558, 0.7608, 11.0954],\n",
|
|
" grad_fn=<MvBackward>), <function test_launch at 0x7fa6269e4430>) \n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"(tensor([2.7431, 2.7431, 2.7431, 2.7431, 2.2016], grad_fn=<AddBackward0>), tensor([0.0503], grad_fn=<AddBackward0>), <function profit at 0x7fa6269e4550>, <function laws_of_motion at 0x7fa6269e44c0>, tensor([-25.6591, -23.1244, -23.5468, -23.5468, -29.4675, 123.8316],\n",
|
|
" grad_fn=<MvBackward>), <function test_launch at 0x7fa6269e4430>) \n",
|
|
"\n",
|
|
"\n",
|
|
"\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"#Get the values from 5 transitions\n",
|
|
"wt1 = compose_recursive_functions(transition_wrapper,2)\n",
|
|
"for f in wt1:\n",
|
|
" result = f(base_data)\n",
|
|
" print(result, \"\\n\"*3)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "ready-quick",
|
|
"metadata": {},
|
|
"source": [
|
|
"Also, maybe I can create a `Model` class that upon construction will capture the necesary constants, functions, etc.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "intensive-forum",
|
|
"metadata": {},
|
|
"source": [
|
|
"# Optimatility conditions"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 54,
|
|
"id": "prospective-mambo",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"#Optimality math\n",
|
|
"def optimality_parts(stocks,debris,profit_fn,laws_motion_fn,launch_fn):\n",
|
|
" #Derivative of the value function with respect to choice functions\n",
|
|
" #this returns derivatives with respect to every launch, so I've removed that\n",
|
|
" fx = jacobian(profit_fn, (stocks,debris,launch_fn(stocks,debris)))[-1][:,0]\n",
|
|
" \n",
|
|
" \n",
|
|
" #The following returns a tuple of tuples of tensors.\n",
|
|
" #the first tuple contains jacobians related to laws of motion for stocks\n",
|
|
" #the second tuple contains jacobians related to laws of motion for debris.\n",
|
|
" #we need the derivatives related to both\n",
|
|
" b = jacobian(laws_of_motion,(stocks,debris,launches(stocks,debris)))\n",
|
|
" B = torch.cat((b[0][2],b[1][2].T),axis=1)\n",
|
|
"\n",
|
|
"\n",
|
|
" return fx, B\n",
|
|
"\n",
|
|
"#Optimality condition\n",
|
|
"def optimality(stocks,debris,profit_fn,laws_motion_fn,launch_fn, iterated_item):\n",
|
|
" #calculate portions of the optimality values\n",
|
|
" fx, B = optimality_parts(stocks,debris,profit_fn,laws_motion_fn,launch_fn)\n",
|
|
"\n",
|
|
" return fx + BETA *B @ iterated_item"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 11,
|
|
"id": "distinguished-consultancy",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"tmp_result = torch.tensor([1.0,2.0,3,4,5,6])"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 12,
|
|
"id": "stylish-plaintiff",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"tensor(49.4968)"
|
|
]
|
|
},
|
|
"execution_count": 12,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"sum(optimality(stocks,debris,profit,laws_of_motion,launches,tmp_result)**2)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "accredited-wales",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Now to set up the recursive set of optimatliy conditions"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 35,
|
|
"id": "steady-chart",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"tensor([-1.3770, 0.8862, 0.6757, 0.6757, 0.7754], grad_fn=<AddBackward0>)\n",
|
|
"tensor(4.1957, grad_fn=<AddBackward0>)\n",
|
|
"\n",
|
|
"tensor([-24.7879, -21.3799, -21.7813, -21.7813, -27.4059],\n",
|
|
" grad_fn=<AddBackward0>)\n",
|
|
"tensor(2771.4756, grad_fn=<AddBackward0>)\n",
|
|
"\n",
|
|
"tensor([-218.6896, -214.1374, -214.7294, -214.7294, -290.5158],\n",
|
|
" grad_fn=<AddBackward0>)\n",
|
|
"tensor(270296.8438, grad_fn=<AddBackward0>)\n",
|
|
"\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"base_data = (stocks,debris, profit, laws_of_motion, torch.ones(6, requires_grad=True),launches)\n",
|
|
"for f in compose_recursive_functions(transition_wrapper,3):\n",
|
|
" result = f(base_data)\n",
|
|
" \n",
|
|
" #unpack results\n",
|
|
" new_stocks, new_debris, profit_fn, laws_motion_fn, transitioned, launch_fn = result\n",
|
|
" \n",
|
|
" optimal = optimality(new_stocks,new_debris,profit_fn,laws_motion_fn,launch_fn,transitioned)\n",
|
|
" print(optimal)\n",
|
|
" print(sum(optimal**2))\n",
|
|
" print()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 46,
|
|
"id": "distributed-mailing",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"def optimality_wrapper(stocks,debris, launches):\n",
|
|
" return optimality(stocks,debris, profit, laws_of_motion, launches, torch.ones(6, requires_grad=True))"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 49,
|
|
"id": "considerable-settle",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"tensor([-0.0452, 0.9548, 0.9548, 0.9548, 0.9548], grad_fn=<AddBackward0>)"
|
|
]
|
|
},
|
|
"execution_count": 49,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"optimality_wrapper(stocks,debris,launches)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 52,
|
|
"id": "second-aquarium",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"tensor(1.)"
|
|
]
|
|
},
|
|
"execution_count": 52,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": []
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "innocent-visibility",
|
|
"metadata": {},
|
|
"source": [
|
|
"Notes so far\n",
|
|
" - This takes $\\frac{\\partial W_t}{\\partial{\\theta_t}$ as given. I need to reframe this to clean that out. \n",
|
|
" - a failed method was just to calculate the partials of W for t = t. That doesn't work.\n",
|
|
" - Becasue the B value is not invertible, you can't just solve for the partials.\n",
|
|
" - note the approach below.\n",
|
|
" - Use part of the launch NN to approximate the gradient conditions.\n",
|
|
" - Complicates things slightly as it would require adding more outputs to the NN\n",
|
|
" - Would allow for PDE solution for value function.\n",
|
|
" - This might be useful for free-entry conditions.\n",
|
|
" - Not very helpful otherwise.\n",
|
|
" - I don't think it adds much to the final analysis.\n",
|
|
" \n",
|
|
" \n",
|
|
" - There is an issue in how to handle the multiple value functions/loss functions\n",
|
|
" - Current status\n",
|
|
" - The launch function (soon to be NN) returns launch choices for each constellation.\n",
|
|
"\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "operating-carpet",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": []
|
|
}
|
|
],
|
|
"metadata": {
|
|
"kernelspec": {
|
|
"display_name": "Python 3",
|
|
"language": "python",
|
|
"name": "python3"
|
|
},
|
|
"language_info": {
|
|
"codemirror_mode": {
|
|
"name": "ipython",
|
|
"version": 3
|
|
},
|
|
"file_extension": ".py",
|
|
"mimetype": "text/x-python",
|
|
"name": "python",
|
|
"nbconvert_exporter": "python",
|
|
"pygments_lexer": "ipython3",
|
|
"version": "3.8.8"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 5
|
|
}
|