From e4b13f594741bffd0e0560df8552b3fcf734e327 Mon Sep 17 00:00:00 2001 From: vinid Date: Sun, 20 Oct 2024 20:55:14 -0400 Subject: [PATCH] fix error and deprecate --- .../Tutorial-Solution-Optimization.ipynb | 176 ++++++ .../Tutorial-Test-Time-Loss-for-Code.ipynb | 547 ++++++++++++++++++ textgrad/engine/base.py | 7 +- textgrad/variable.py | 7 +- 4 files changed, 731 insertions(+), 6 deletions(-) create mode 100644 examples/notebooks/experimental_engines/Tutorial-Solution-Optimization.ipynb create mode 100644 examples/notebooks/experimental_engines/Tutorial-Test-Time-Loss-for-Code.ipynb diff --git a/examples/notebooks/experimental_engines/Tutorial-Solution-Optimization.ipynb b/examples/notebooks/experimental_engines/Tutorial-Solution-Optimization.ipynb new file mode 100644 index 0000000..5741052 --- /dev/null +++ b/examples/notebooks/experimental_engines/Tutorial-Solution-Optimization.ipynb @@ -0,0 +1,176 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "fe4cb25e4fb45586", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "## Tutorial: Running Solution Optimization\n", + "\n", + "![TextGrad](https://github.com/vinid/data/blob/master/logo_full.png?raw=true)\n", + "\n", + "An autograd engine -- for textual gradients!\n", + "\n", + "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/zou-group/TextGrad/blob/main/examples/notebooks/Prompt-Optimization.ipynb)\n", + "[![GitHub license](https://img.shields.io/badge/License-MIT-blue.svg)](https://lbesson.mit-license.org/)\n", + "[![Arxiv](https://img.shields.io/badge/arXiv-2406.07496-B31B1B.svg)](https://arxiv.org/abs/2406.07496)\n", + "[![Documentation Status](https://readthedocs.org/projects/textgrad/badge/?version=latest)](https://textgrad.readthedocs.io/en/latest/?badge=latest)\n", + "[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/textgrad)](https://pypi.org/project/textgrad/)\n", + "[![PyPI](https://img.shields.io/pypi/v/textgrad)](https://pypi.org/project/textgrad/)\n", + "\n", + "**Objectives:**\n", + "\n", + "* In this tutorial, we will implement a solution optimization pipeline.\n", + "\n", + "**Requirements:**\n", + "\n", + "* You need to have an OpenAI API key to run this tutorial. This should be set as an environment variable as OPENAI_API_KEY.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "1f6e021565d0c914", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/federicobianchi/.pyenv/versions/3.11.9/lib/python3.11/site-packages/pydantic/_internal/_config.py:341: UserWarning: Valid config keys have changed in V2:\n", + "* 'underscore_attrs_are_private' has been removed\n", + " warnings.warn(message, UserWarning)\n" + ] + } + ], + "source": [ + "#!pip install textgrad # you might need to restart the notebook after installing textgrad\n", + "\n", + "from dotenv import load_dotenv\n", + "load_dotenv()\n", + "import textgrad as tg\n", + "tg.set_backward_engine(tg.get_engine(\"experimental:gpt-4o\"))\n", + "\n", + "initial_solution = \"\"\"To solve the equation 3x^2 - 7x + 2 = 0, we use the quadratic formula:\n", + "x = (-b ± √(b^2 - 4ac)) / 2a\n", + "a = 3, b = -7, c = 2\n", + "x = (7 ± √((-7)^2 + 4(3)(2))) / 6\n", + "x = (7 ± √73) / 6\n", + "The solutions are:\n", + "x1 = (7 + √73)\n", + "x2 = (7 - √73)\"\"\"\n", + "\n", + "solution = tg.Variable(initial_solution,\n", + " requires_grad=True,\n", + " role_description=\"solution to the math question\")\n", + "\n", + "loss_system_prompt = tg.Variable(\"\"\"You will evaluate a solution to a math question. \n", + "Do not attempt to solve it yourself, do not give a solution, only identify errors. Be super concise.\"\"\",\n", + " requires_grad=False,\n", + " role_description=\"system prompt\")\n", + " \n", + "loss_fn = tg.TextLoss(loss_system_prompt)\n", + "optimizer = tg.TGD([solution])" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "169c07c1-a01d-4309-9fd4-a6f9fb52daf4", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Variable(value=There are calculation errors in applying the quadratic formula and in the final solutions:\n", + "\n", + "1. **Quadratic Formula Error**: The discriminant calculation is incorrect. It should be \\( b^2 - 4ac \\), not \\( b^2 + 4ac \\).\n", + "\n", + "2. **Discriminant Error**: The discriminant is calculated incorrectly. It should be \\( (-7)^2 - 4(3)(2) = 49 - 24 = 25 \\).\n", + "\n", + "3. **Square Root Error**: The square root of the discriminant should be \\( \\sqrt{25} = 5 \\), not \\( \\sqrt{73} \\).\n", + "\n", + "4. **Solutions Error**: The solutions do not incorporate the division by 6. The correct expressions for the solutions should involve \\((7 ± 5) / 6\\).\n", + "\n", + "5. **Final Numerical Error**: The final solutions provided do not simplify after dividing by 6. Adjust the ± part before dividing., role=response from the language model, grads=set())" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "loss = loss_fn(solution)\n", + "loss" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "a2fce8c1-6838-4aaf-b830-de34effe44db", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "To solve the equation 3x^2 - 7x + 2 = 0, we use the quadratic formula:\n", + "\\[ x = \\frac{-b \\pm \\sqrt{b^2 - 4ac}}{2a} \\]\n", + "a = 3, b = -7, c = 2\n", + "\\[ x = \\frac{7 \\pm \\sqrt{(-7)^2 - 4(3)(2)}}{6} \\]\n", + "\\[ x = \\frac{7 \\pm \\sqrt{25}}{6} \\]\n", + "\\[ x = \\frac{7 \\pm 5}{6} \\]\n", + "The solutions are:\n", + "\\[ x1 = \\frac{7 + 5}{6} = 2 \\]\n", + "\\[ x2 = \\frac{7 - 5}{6} = \\frac{1}{3} \\]\n" + ] + } + ], + "source": [ + "loss.backward()\n", + "optimizer.step()\n", + "print(solution.value)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4e9e4cb2-1c6e-499f-bfd6-efa47461f6a6", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/notebooks/experimental_engines/Tutorial-Test-Time-Loss-for-Code.ipynb b/examples/notebooks/experimental_engines/Tutorial-Test-Time-Loss-for-Code.ipynb new file mode 100644 index 0000000..325b1b3 --- /dev/null +++ b/examples/notebooks/experimental_engines/Tutorial-Test-Time-Loss-for-Code.ipynb @@ -0,0 +1,547 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "fecb2fb5-b87f-4428-8175-e3a46fe77371", + "metadata": {}, + "source": [ + "## Tutorial: Defining a new test-time loss and optimizing code.\n", + "\n", + "![TextGrad](https://github.com/vinid/data/blob/master/logo_full.png?raw=true)\n", + "\n", + "An autograd engine -- for textual gradients!\n", + "\n", + "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/zou-group/TextGrad/blob/main/examples/notebooks/Prompt-Optimization.ipynb)\n", + "[![GitHub license](https://img.shields.io/badge/License-MIT-blue.svg)](https://lbesson.mit-license.org/)\n", + "[![Arxiv](https://img.shields.io/badge/arXiv-2406.07496-B31B1B.svg)](https://arxiv.org/abs/2406.07496)\n", + "[![Documentation Status](https://readthedocs.org/projects/textgrad/badge/?version=latest)](https://textgrad.readthedocs.io/en/latest/?badge=latest)\n", + "[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/textgrad)](https://pypi.org/project/textgrad/)\n", + "[![PyPI](https://img.shields.io/pypi/v/textgrad)](https://pypi.org/project/textgrad/)\n", + "\n", + "**Objectives:**\n", + "\n", + "* In this tutorial, we will do a quick walkthrough around how to define a simple test time loss in TextGrad and optimize a variable of interest.\n", + "\n", + "**Requirements:**\n", + "\n", + "* You need to have an OpenAI API key to run this tutorial. This should be set as an environment variable as OPENAI_API_KEY.\n", + "\n", + "We first define some utilities and a set of test cases." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "7add4547-4278-411b-a827-79be521851f1", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/federicobianchi/.pyenv/versions/3.11.9/lib/python3.11/site-packages/pydantic/_internal/_config.py:341: UserWarning: Valid config keys have changed in V2:\n", + "* 'underscore_attrs_are_private' has been removed\n", + " warnings.warn(message, UserWarning)\n" + ] + } + ], + "source": [ + "!pip install textgrad # you might need to restart the notebook after installing textgrad\n", + "\n", + "import textgrad as tg\n", + "import random\n", + "import time" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "47b0802c-3fb8-4a24-9f27-109ccb1101d6", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from dotenv import load_dotenv\n", + "load_dotenv()" + ] + }, + { + "cell_type": "markdown", + "id": "0bc1891d-cbc0-4e46-a618-2925fe5f122c", + "metadata": {}, + "source": [ + "### Utilities to run the code, and test cases" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "54c93006-dc0e-4882-bbb6-a248afed404b", + "metadata": {}, + "outputs": [], + "source": [ + "# We'll use below utilities to run a python function.\n", + "from IPython.core.interactiveshell import InteractiveShell\n", + "\n", + "def run_function_in_interpreter(func_code):\n", + " raise Exception(\"This function will run the code returned by GPT-4o. Remove this if you'd like to run the code!\")\n", + " interpreter = InteractiveShell.instance()\n", + " \n", + " interpreter.run_cell(func_code, store_history=False, silent=True)\n", + " \n", + " func_name = func_code.split(\"def \")[1].split(\"(\")[0].strip()\n", + " func = interpreter.user_ns[func_name]\n", + " \n", + " return func\n", + "\n", + "\n", + "def test_longest_increasing_subsequence(fn):\n", + " nums = [10, 22, 9, 33, 21, 50, 41, 60]\n", + " assert fn(nums) == 5\n", + "\n", + " nums = [7, 2, 1, 3, 8, 4, 9, 6, 5]\n", + " assert fn(nums) == 4\n", + "\n", + " nums = [5, 4, 3, 2, 1]\n", + " assert fn(nums) == 1\n", + "\n", + " nums = [1, 2, 3, 4, 5]\n", + " assert fn(nums) == 5\n", + "\n", + " nums = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]\n", + " assert fn(nums) == 4\n", + "\n", + " nums = [10, 9, 2, 5, 3, 7, 101, 18]\n", + " assert fn(nums) == 4\n", + "\n", + " nums = [0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15]\n", + " assert fn(nums) == 6\n", + "\n", + " nums = [7, 7, 7, 7, 7, 7, 7]\n", + " assert fn(nums) == 1\n", + "\n", + " nums = [20, 25, 47, 35, 56, 68, 98, 101, 212, 301, 415, 500]\n", + " assert fn(nums) == 11\n", + "\n", + " nums = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]\n", + " assert fn(nums) == 1\n", + "\n", + " print(\"All test cases passed!\")" + ] + }, + { + "cell_type": "markdown", + "id": "5c6c90b1-24b8-4f78-8cd3-5c01c02dbf99", + "metadata": {}, + "source": [ + "## Problem: Improving a code snippet.\n", + "We have a simple problem, and an initial solution that does not run quite fast. We first test this solution and look at the wall clock time." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "88256034-6c88-4a81-ac41-7e6f14abc89e", + "metadata": {}, + "outputs": [], + "source": [ + "problem_text = \"\"\"Longest Increasing Subsequence (LIS)\n", + "\n", + "Problem Statement:\n", + "Given a sequence of integers, find the length of the longest subsequence that is strictly increasing. A subsequence is a sequence that can be derived from another sequence by deleting some or no elements without changing the order of the remaining elements.\n", + "\n", + "Input:\n", + "The input consists of a list of integers representing the sequence.\n", + "\n", + "Output:\n", + "The output should be an integer representing the length of the longest increasing subsequence.\"\"\"\n", + "\n", + "initial_solution = \"\"\"\n", + "def longest_increasing_subsequence(nums):\n", + " n = len(nums)\n", + " dp = [1] * n\n", + " \n", + " for i in range(1, n):\n", + " for j in range(i):\n", + " if nums[i] > nums[j]:\n", + " dp[i] = max(dp[i], dp[j] + 1)\n", + " \n", + " max_length = max(dp)\n", + " lis = []\n", + " \n", + " for i in range(n - 1, -1, -1):\n", + " if dp[i] == max_length:\n", + " lis.append(nums[i])\n", + " max_length -= 1\n", + " \n", + " return len(lis[::-1])\n", + "\"\"\"\n", + "\n", + "# Generate a random test case\n", + "def generate_random_test_case(size, min_value, max_value):\n", + " return [random.randint(min_value, max_value) for _ in range(size)]\n", + "\n", + "# Test the function with a random test case\n", + "size = 10000 # Adjust the size as needed\n", + "min_value = 1\n", + "max_value = 1000\n", + "\n", + "nums = generate_random_test_case(size, min_value, max_value)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "331469ff-6106-4f7b-a761-f9d3cf91f2b1", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Test Case Size: 10000\n", + "Longest Increasing Subsequence Length: 181\n", + "Runtime: 2.98286 seconds\n", + "All test cases passed!\n" + ] + } + ], + "source": [ + "longest_increasing_subsequence = run_function_in_interpreter(initial_solution)\n", + "\n", + "start_time = time.time()\n", + "lis = longest_increasing_subsequence(nums)\n", + "end_time = time.time()\n", + "\n", + "print(f\"Test Case Size: {size}\")\n", + "print(f\"Longest Increasing Subsequence Length: {lis}\")\n", + "print(f\"Runtime: {end_time - start_time:.5f} seconds\")\n", + "\n", + "# Test for all test cases\n", + "test_longest_increasing_subsequence(longest_increasing_subsequence)" + ] + }, + { + "cell_type": "markdown", + "id": "db85e3b2-fed6-4ca2-a7a4-13183a0a9273", + "metadata": {}, + "source": [ + "## TextGrad to optimize code!\n", + "Here, we will optimize the code instance. We first define the variables and instantiate the optimizer, then define our loss function, and finally update the code!" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "4ded5b66-3250-4a85-b045-625084a5c214", + "metadata": {}, + "outputs": [], + "source": [ + "llm_engine = tg.get_engine(\"experimental:gpt-4o\")\n", + "tg.set_backward_engine(llm_engine)\n", + "\n", + "# Code is the variable of interest we want to optimize -- so requires_grad=True\n", + "code = tg.Variable(value=initial_solution,\n", + " requires_grad=True,\n", + " role_description=\"code instance to optimize\")\n", + "\n", + "# We are not interested in optimizing the problem -- so requires_grad=False\n", + "problem = tg.Variable(problem_text, \n", + " requires_grad=False, \n", + " role_description=\"the coding problem\")\n", + "\n", + "# Let TGD know to update code!\n", + "optimizer = tg.TGD(parameters=[code])" + ] + }, + { + "cell_type": "markdown", + "id": "207fe9d3-3103-4abe-b0b7-482b99311ff1", + "metadata": {}, + "source": [ + "## Defining a loss function with the FormattedLLMCall operation\n", + "\n", + "Here, we define a structured loss function. In particular, we want the following format:\n", + "\n", + "```\n", + "{instruction}\n", + "Problem: {problem}\n", + "Current Code: {code}\n", + "```\n", + "\n", + "`FormattedLLMCall` helps us define loss functions like this, while keeping track of the children variables." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "40b86157-ed34-4a32-9461-dde35755ef72", + "metadata": {}, + "outputs": [], + "source": [ + "# The system prompt that will guide the behavior of the loss function.\n", + "loss_system_prompt = \"You are a smart language model that evaluates code snippets. You do not solve problems or propose new code snippets, only evaluate existing solutions critically and give very concise feedback.\"\n", + "loss_system_prompt = tg.Variable(loss_system_prompt, requires_grad=False, role_description=\"system prompt to the loss function\")\n", + "\n", + "# The instruction that will be the prefix\n", + "instruction = \"\"\"Think about the problem and the code snippet. Does the code solve the problem? What is the runtime complexity?\"\"\"\n", + "\n", + "# The format string and setting up the call\n", + "format_string = \"{instruction}\\nProblem: {{problem}}\\nCurrent Code: {{code}}\"\n", + "format_string = format_string.format(instruction=instruction)\n", + "\n", + "fields = {\"problem\": None, \"code\": None}\n", + "formatted_llm_call = tg.autograd.FormattedLLMCall(engine=llm_engine,\n", + " format_string=format_string,\n", + " fields=fields,\n", + " system_prompt=loss_system_prompt)\n", + "\n", + "# Finally, the loss function\n", + "def loss_fn(problem: tg.Variable, code: tg.Variable) -> tg.Variable:\n", + " inputs = {\"problem\": problem, \"code\": code}\n", + " \n", + " return formatted_llm_call(inputs=inputs,\n", + " response_role_description=f\"evaluation of the {code.get_role_description()}\")\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "624c9847-633b-4e9c-9722-e433e25593b7", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Yes, the code correctly solves the problem of finding the length of the longest increasing subsequence. It uses dynamic programming to build a `dp` array where `dp[i]` represents the length of the longest increasing subsequence ending at index `i`. The code then reconstructs the LIS by iterating backwards through the `dp` array.\n", + "\n", + "The runtime complexity of the code is \\(O(n^2)\\), where \\(n\\) is the length of the input list `nums`. This is due to the nested loop structure: the outer loop runs \\(n-1\\) iterations and the inner loop can run up to \\(i\\) iterations for each outer loop iteration.\n" + ] + } + ], + "source": [ + "# Let's do the forward pass for the loss function.\n", + "loss = loss_fn(problem, code)\n", + "print(loss.value)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4afc96ed-d37d-4301-a942-b6c92d9b1aac", + "metadata": {}, + "outputs": [], + "source": [ + "# Let's visualize our computation graph.\n", + "loss.generate_graph()" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "d63b1bbd-c638-4e16-8ffe-a4a5ad45b3b8", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{Variable(value=To improve the objective function, which focuses on the evaluation of the given code snippet, consider the following feedback:\n", + "\n", + "1. **Improve Runtime Complexity**: The current implementation of the function `longest_increasing_subsequence(nums)` has a runtime complexity of \\(O(n^2)\\). Since the typical LIS problem can be solved more efficiently using binary search alongside dynamic programming, an \\(O(n \\log n)\\) solution can be employed. This involves maintaining a list to store active subsequences and using binary search to determine the appropriate position to update, significantly improving the performance on large datasets.\n", + "\n", + "2. **Avoid Full LIS Reconstruction**: The current code reconstructs the entire longest increasing subsequence by iterating backwards through the `dp` array to find numbers that match the LIS length. Since only the length of the LIS is required as output, the reconstruction step can be omitted, saving additional computation time and memory use.\n", + "\n", + "3. **Variable Naming**: Improve clarity and maintainability by using more descriptive variable names. For example, rename `dp` to `lis_lengths` or `ends`, making its purpose clearer.\n", + "\n", + "4. **Code Efficiency**: The reconstruction section of the code uses `lis[::-1]`, which reverses the list before taking its length. This reversal is unnecessary, as the final length is unaffected by order, and thus can be removed. This would slightly enhance runtime efficiency, albeit insignificantly compared to the other changes suggested.\n", + "\n", + "5. **Add Input Validation**: Introduce checks to ensure that the input `nums` is valid (e.g., a list of integers). This can prevent unexpected behavior or errors if the function is used in unintended ways.\n", + "\n", + "6. **Edge Cases**: Consider explicit handling of edge cases, such as when the input list `nums` is empty. Currently, the code relies implicitly on the `max(dp)` function; however, addressing these scenarios directly at the beginning could improve code robustness and clarity.\n", + "\n", + "Incorporating the suggestions above would likely lead to significant improvements in both the performance and readability of the code., role=feedback to code instance to optimize, grads=set())}\n" + ] + } + ], + "source": [ + "# Let's look at the gradients!\n", + "loss.backward()\n", + "print(code.gradients)" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "caad85e4-b449-495b-a845-3b372e00dfd6", + "metadata": {}, + "outputs": [], + "source": [ + "# Let's update the code\n", + "optimizer.step()" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "77602cbf-7cd6-4f59-b69e-f7fe7d351731", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Longest Increasing Subsequence Length: 181\n", + "Runtime: 0.00285 seconds\n", + "All test cases passed!\n" + ] + } + ], + "source": [ + "# Hopefully, we should get much better runtime!\n", + "longest_increasing_subsequence = run_function_in_interpreter(code.value)\n", + "\n", + "start_time = time.time()\n", + "lis = longest_increasing_subsequence(nums)\n", + "end_time = time.time()\n", + "\n", + "print(f\"Longest Increasing Subsequence Length: {lis}\")\n", + "print(f\"Runtime: {end_time - start_time:.5f} seconds\")\n", + "\n", + "test_longest_increasing_subsequence(longest_increasing_subsequence)" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "ae584c91-5dc3-4b37-9b88-4a34f7b6f62a", + "metadata": {}, + "outputs": [], + "source": [ + "# Let's do one more iteration\n", + "optimizer.zero_grad()\n", + "loss = loss_fn(problem, code)\n", + "loss.backward()\n", + "optimizer.step()" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "aeb54d4e-0d9f-4768-9b8b-06ea97657751", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Longest Increasing Subsequence Length: 181\n", + "Runtime: 0.00248 seconds\n", + "All test cases passed!\n" + ] + } + ], + "source": [ + "longest_increasing_subsequence = run_function_in_interpreter(code.value)\n", + "\n", + "start_time = time.time()\n", + "lis = longest_increasing_subsequence(nums)\n", + "end_time = time.time()\n", + "\n", + "print(f\"Longest Increasing Subsequence Length: {lis}\")\n", + "print(f\"Runtime: {end_time - start_time:.5f} seconds\")\n", + "\n", + "test_longest_increasing_subsequence(longest_increasing_subsequence)" + ] + }, + { + "cell_type": "markdown", + "id": "54efbf37-4c7a-486c-8112-966e6b34e23c", + "metadata": {}, + "source": [ + "## Optimized code, much faster!" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "f6115288-cb26-4008-9882-a5af60e49c3f", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "from bisect import bisect_left\n", + "\n", + "def longest_increasing_subsequence(nums):\n", + " # Handle edge cases where input list is empty or has only one element.\n", + " if not nums:\n", + " return 0\n", + " if len(nums) == 1:\n", + " return 1\n", + "\n", + " # subseq_tail maintains the smallest last terms of all increasing subsequences\n", + " subseq_tail = []\n", + "\n", + " for num in nums:\n", + " # Find position where num can either replace an existing element\n", + " # to maintain the smallest possible last term or be appended\n", + " position = bisect_left(subseq_tail, num)\n", + " if position == len(subseq_tail):\n", + " subseq_tail.append(num)\n", + " else:\n", + " subseq_tail[position] = num\n", + "\n", + " # The length of subseq_tail corresponds to the length of the longest increasing subsequence\n", + " return len(subseq_tail)\n" + ] + } + ], + "source": [ + "print(code.value)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "202c93c1-e7b6-4b89-9851-8d5ee07527d9", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/textgrad/engine/base.py b/textgrad/engine/base.py index c036ee5..a7a9baa 100644 --- a/textgrad/engine/base.py +++ b/textgrad/engine/base.py @@ -7,9 +7,10 @@ class EngineLM(ABC): system_prompt: str = "You are a helpful, creative, and smart assistant." model_string: str - def __init__(self): - warnings.warn("This Engine class is depreacted and will be removed in future version. Please use experimental engines. See https://github.com/zou-group/textgrad/pull/120", DeprecationWarning, - stacklevel=2) + warnings.warn( + "This Engine class is depreacted and will be removed in future version. Please use experimental engines. See https://github.com/zou-group/textgrad/pull/120", + DeprecationWarning, + stacklevel=2) @abstractmethod def generate(self, prompt, system_prompt=None, **kwargs): diff --git a/textgrad/variable.py b/textgrad/variable.py index 01b9020..2c15529 100644 --- a/textgrad/variable.py +++ b/textgrad/variable.py @@ -2,6 +2,7 @@ from textgrad.engine import EngineLM from typing import List, Set, Dict import httpx +import numpy from collections import defaultdict from functools import partial from .config import SingletonBackwardEngine @@ -39,9 +40,9 @@ def __init__( if (not requires_grad) and (len(_predecessor_requires_grad) > 0): raise Exception("If the variable does not require grad, none of its predecessors should require grad." f"In this case, following predecessors require grad: {_predecessor_requires_grad}") - - assert type(value) in [str, bytes, int], "Value must be a string, int, or image (bytes). Got: {}".format(type(value)) - if isinstance(value, int): + + assert type(value) in [str, bytes, int, numpy.int64], "Value must be a string, int, or image (bytes). Got: {}".format(type(value)) + if isinstance(value, int) or isinstance(value, numpy.int64): value = str(value) # We'll currently let "empty variables" slide, but we'll need to handle this better in the future. # if value == "" and image_path == "":