I am working on a Python project where I calculate and plot a graph of Aliquot sequence. I created a list.py script that displays the numbers in the .db file and it turns out the numbers are correctly computed and saved to a database. However, when I try to plot the sequence using graph.py, I encounter the following error:
Exception in Tkinter callback
AttributeError: 'int' object has no attribute 'log'
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "C:Python312Libtkinter__init__.py", line 1967, in __call__
return self.func(*args)
^^^^^^^^^^^^^^^^
File "graph.py", line 94, in plot_graph
plot_sequence(sequence, log_scale=True, log_base=log_base)
File "graph.py", line 35, in plot_sequence
transformed_sequence = np.log(sequence) / np.log(log_base)
^^^^^^^^^^^^^^^^
TypeError: loop of ufunc does not support argument 0 of type int which has no callable log method
Here is the graph.py script:
import os
import sqlite3
import tkinter as tk
from tkinter import filedialog, messagebox
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.animation import FuncAnimation
def fetch_sequence_from_db(db_path):
try:
conn = sqlite3.connect(db_path)
c = conn.cursor()
c.execute("SELECT sequence FROM sequences")
sequence_str = c.fetchone()[0]
sequence = [int(x) for x in sequence_str.strip('][').split(', ')]
conn.close()
return sequence
except Exception as e:
messagebox.showerror("Error", f"Failed to read database: {e}")
return None
def format_scientific(number):
exponent = int(np.log10(number))
base = number / 10**exponent
return f"{base:.1f} x 10^{exponent}"
def plot_sequence(sequence, log_scale=False, log_base=None):
fig, ax = plt.subplots(figsize=(10, 5))
if log_scale and log_base:
transformed_sequence = np.log(sequence) / np.log(log_base)
ylabel = f"log_{log_base}(Number)"
else:
transformed_sequence = sequence
ylabel = "Number"
ax.set_title("Aliquot Sequence")
ax.set_xlabel("Iteration")
ax.set_ylabel(ylabel)
ax.grid(True)
ax.set_xlim(0, len(transformed_sequence) - 1)
line, = ax.plot([], [], linestyle='-', color='b')
def update(frame):
line.set_data(range(frame + 1), transformed_sequence[:frame + 1])
ax.relim()
ax.autoscale_view()
return line,
ani = FuncAnimation(fig, update, frames=len(transformed_sequence), blit=True, interval=10, repeat=False)
# Find and format the largest number
largest_number = max(sequence)
formatted_largest_number = format_scientific(largest_number)
# Display the largest number on the graph
plt.text(0.5, 1.05, f"Largest number: {formatted_largest_number}", ha='center', va='center', transform=ax.transAxes, fontsize=12)
plt.subplots_adjust(left=0.05, right=0.95) # Adjusting the left margin to make the 0th iteration close to the border
plt.show()
def select_file():
file_path = filedialog.askopenfilename(
initialdir=os.path.join(os.path.dirname(os.path.abspath(__file__)), "Number_Sequences"),
title="Select a Database File",
filetypes=(("Database Files", "*.db"), ("All Files", "*.*"))
)
if file_path:
entry_file_path.delete(0, tk.END)
entry_file_path.insert(0, file_path)
def plot_graph():
db_path = entry_file_path.get()
if not os.path.exists(db_path):
messagebox.showerror("Error", "File not found.")
return
sequence = fetch_sequence_from_db(db_path)
if not sequence:
return
if var_log_transform.get():
try:
log_base = float(entry_log_base.get())
except ValueError:
messagebox.showerror("Error", "Invalid log base.")
return
plot_sequence(sequence, log_scale=True, log_base=log_base)
else:
plot_sequence(sequence, log_scale=False)
# Setting up the Tkinter window
root = tk.Tk()
root.title("Aliquot Sequence Grapher")
frame = tk.Frame(root)
frame.pack(padx=10, pady=10)
label_file_path = tk.Label(frame, text="Database File:")
label_file_path.grid(row=0, column=0, sticky="e")
entry_file_path = tk.Entry(frame, width=40)
entry_file_path.grid(row=0, column=1, padx=5)
button_browse = tk.Button(frame, text="Browse", command=select_file)
button_browse.grid(row=0, column=2, padx=5)
var_log_transform = tk.BooleanVar(value=True)
check_log_transform = tk.Checkbutton(frame, text="Log Transform", variable=var_log_transform)
check_log_transform.grid(row=1, column=0, columnspan=3)
label_log_base = tk.Label(frame, text="Log Base:")
label_log_base.grid(row=2, column=0, sticky="e")
entry_log_base = tk.Entry(frame, width=10)
entry_log_base.grid(row=2, column=1, padx=5, sticky="w")
entry_log_base.insert(0, "10")
button_plot = tk.Button(frame, text="Plot Graph", command=plot_graph)
button_plot.grid(row=3, column=0, columnspan=3, pady=10)
root.mainloop()
This is the list of numbers from the list.py. The number of the iteration is shown only in the list.py script:
1 : 276
2 : 396
3 : 696
4 : 1104
5 : 1872
6 : 3770
7 : 3790
8 : 3050
9 : 2716
10 : 2772
11 : 5964
12 : 10164
13 : 19628
14 : 19684
15 : 22876
16 : 26404
17 : 30044
18 : 33796
19 : 38780
20 : 54628
21 : 54684
22 : 111300
23 : 263676
24 : 465668
25 : 465724
26 : 465780
27 : 1026060
28 : 2325540
29 : 5335260
30 : 11738916
31 : 23117724
32 : 45956820
33 : 121129260
34 : 266485716
35 : 558454764
36 : 1092873236
37 : 1470806764
38 : 1471882804
39 : 1642613196
40 : 2737688884
41 : 2740114636
42 : 2791337780
43 : 4946860492
44 : 4946860548
45 : 9344070652
46 : 9344070708
47 : 15573451404
48 : 27078171764
49 : 27284104204
50 : 27410152084
51 : 27410152140
52 : 76787720100
53 : 220578719452
54 : 254903331620
55 : 361672366300
56 : 603062136740
57 : 921203207260
58 : 1381419996068
59 : 1395444575644
60 : 1395478688996
61 : 1395546402460
62 : 2069258468900
63 : 3065057872156
64 : 3277068463844
65 : 3429776547484
66 : 3597527970596
67 : 4028517592540
68 : 5641400009252
69 : 5641400009308
70 : 5641400009364
71 : 9709348326636
72 : 16331909651988
73 : 31948891146732
74 : 54770416120644
75 : 100509779504316
76 : 208751080955844
77 : 388416032284476
78 : 749365894850244
79 : 1414070378301756
80 : 2556878765995204
81 : 2556878765995260
82 : 6726041614128900
83 : 15626498692840700
84 : 23762659088671300
85 : 35168735451235260
86 : 78257359358590020
87 : 186897487211247036
88 : 340813223900632644
89 : 592585414385033916
90 : 1326583294186844484
91 : 2594892903616159356
92 : 4946738730471899844
93 : 8244565422068579772
94 : 13740942370114299844
95 : 13780400058385352252
96 : 13780400058385352308
97 : 14272557426581383244
98 : 14272557426581383300
99 : 21155073391000330684
100 : 21374326697892540932
101 : 22138822441861473292
I also have the same file but with only 70 iterations with the same numbers and it plots the graph just fine. Here is the main.py script that calculates the Aliquot Sequence if needed:
import os
import math
import sqlite3
# Function to get all divisors of a given number
def get_divisors(n):
divisors = [1]
for i in range(2, int(math.sqrt(n)) + 1):
if n % i == 0:
divisors.append(i)
if i != n // i:
divisors.append(n // i)
if n > 1:
divisors.append(n)
return sorted(divisors)
# Function to calculate the sum of divisors for a given number
def sum_of_divisors(n):
return sum(get_divisors(n)) - n
# Function to calculate the Aliquot Sequence for a given number
def aliquot_sequence(start_number, iterations=None):
sequence = [start_number]
current_iteration = 1
while True:
next_number = sum_of_divisors(sequence[-1])
sequence.append(next_number)
print(f"Iteration {current_iteration}: {next_number}")
if next_number == 1 or next_number in sequence[:-1] or next_number == sum_of_divisors(next_number):
break
if iterations and current_iteration >= iterations:
break
current_iteration += 1
return sequence
# Function to save the result to the database
def save_to_database(number, sequence, iterations=None, continue_from_file=None):
current_dir = os.path.dirname(os.path.abspath(__file__))
folder_name = os.path.join(current_dir, "Number Sequences")
if not os.path.exists(folder_name):
try:
os.makedirs(folder_name)
except OSError as e:
print(f"Error creating folder: {e}")
return
if continue_from_file:
db_file_path = continue_from_file
original_name, _ = os.path.splitext(os.path.basename(continue_from_file))
original_number, original_iterations = original_name.split('_')
original_iterations = int(original_iterations)
total_iterations = original_iterations + (len(sequence) - 1)
if sequence[-1] == 1 or sequence[-1] in sequence[:-1] or sequence[-1] == sum_of_divisors(sequence[-1]):
final_file_name = f"{number}.db"
else:
final_file_name = f"{number}_{total_iterations}.db"
final_db_file_path = os.path.join(folder_name, final_file_name)
else:
if iterations:
file_name = f"{number}_{iterations}.db"
else:
file_name = f"{number}.db"
db_file_path = os.path.join(folder_name, file_name)
try:
conn = sqlite3.connect(db_file_path)
c = conn.cursor()
c.execute('''CREATE TABLE IF NOT EXISTS sequences (number INTEGER, sequence TEXT)''')
if continue_from_file:
c.execute('''SELECT sequence FROM sequences''')
old_sequence_str = c.fetchone()[0]
old_sequence = [int(x) for x in old_sequence_str.strip('][').split(', ')]
combined_sequence = old_sequence + sequence[1:] # Exclude the initial number from new sequence
c.execute('''UPDATE sequences SET sequence = ? WHERE number = ?''', (str(combined_sequence), number))
else:
c.execute('''INSERT INTO sequences (number, sequence) VALUES (?, ?)''', (number, str(sequence)))
conn.commit()
conn.close()
if continue_from_file and db_file_path != final_db_file_path:
try:
os.rename(db_file_path, final_db_file_path)
except OSError as e:
print(f"Error renaming file to final name: {e}")
except sqlite3.Error as e:
print(f"Database error: {e}")
return
# Function to check if number exists in the database
def number_exists_in_database(number, iterations=None):
current_dir = os.path.dirname(os.path.abspath(__file__))
folder_name = os.path.join(current_dir, "Number Sequences")
if os.path.exists(folder_name):
for file in os.listdir(folder_name):
if file.endswith(".db"):
db_name, _ = os.path.splitext(file)
split_name = db_name.split('_')
db_number = int(split_name[0])
db_iterations = None if len(split_name) == 1 else int(split_name[1])
if db_number == number and db_iterations == iterations:
return True
return False
# Main function
def main():
# Get input from the user
number = int(input("Enter a number to calculate its Aliquot Sequence: "))
iterations = input("Enter 'auto' to calculate until stopped automatically, or enter the number of iterations: ")
if iterations.lower() == 'auto':
iterations = None
else:
iterations = int(iterations)
continue_calculation = input("Do you want to continue calculation from where you left? (yes/no): ")
if continue_calculation.lower() == 'yes':
continue_from_file = input("Enter the file name to continue from: ")
continue_from_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), "Number Sequences", continue_from_file)
if not os.path.exists(continue_from_file):
print("File not found.")
return
conn = sqlite3.connect(continue_from_file)
c = conn.cursor()
c.execute('''SELECT sequence FROM sequences''')
sequence_str = c.fetchone()[0]
sequence = [int(x) for x in sequence_str.strip('][').split(', ')]
conn.close()
start_number = sequence[-1] # Start from the last number in the sequence
else:
continue_from_file = None
start_number = number
# Check if number exists in the database
if number_exists_in_database(number, iterations):
print("Sequence already exists in the database.")
return
# Calculate Aliquot Sequence
new_sequence = aliquot_sequence(start_number, iterations)
# Print the sequence
print(f"Aliquot Sequence for {number}: {new_sequence}")
# Save the sequence to the database
save_to_database(number, new_sequence, iterations, continue_from_file)
print("Sequence saved to database.")
if __name__ == "__main__":
main()
Ensured log_base is converted to float before being used.
1