Optimering i Python med E-serier
Optimering i Python med E-serier
Försöker få till ett verktyg i Python för design av enklare kretsar så som spänningsdelare med mera utifrån komponentvärden enligt specificerad serie.
Vartannat E48-värde och lägre serier är enkelt då jag kan beräkna alla kombinationer och sedan ta ut de kombinationer som ger minst fel. Men för större serier (E192) och kretsar med många komponenter räcker inte minnet till då antalet kombinationer blir väldigt många.
Därav söker jag en funktion eller klass som kan ta ett matematiskt uttryck (objekt) och villkor/restriktion (komponentvärden/serier) som optimerar mot vilka kombinationer som ger minst fel mot önskat resultat.
Tyvärr är jag nybörjare med Python och programmering gör jag väldigt sällan varav jag hellre använder något enkelt än resurseffektivt.
Har ni andra några idéer över Python-verktyg som skulle kunna hjälpa mig eller någon algoritm som går enkelt att implementera?
Vartannat E48-värde och lägre serier är enkelt då jag kan beräkna alla kombinationer och sedan ta ut de kombinationer som ger minst fel. Men för större serier (E192) och kretsar med många komponenter räcker inte minnet till då antalet kombinationer blir väldigt många.
Därav söker jag en funktion eller klass som kan ta ett matematiskt uttryck (objekt) och villkor/restriktion (komponentvärden/serier) som optimerar mot vilka kombinationer som ger minst fel mot önskat resultat.
Tyvärr är jag nybörjare med Python och programmering gör jag väldigt sällan varav jag hellre använder något enkelt än resurseffektivt.
Har ni andra några idéer över Python-verktyg som skulle kunna hjälpa mig eller någon algoritm som går enkelt att implementera?
Re: Optimering i Python med E-serier
Varför spara resultaten av alla beräkningar? Du är väl bara intresserad av den kombination som är bäst?
Beräkna alltså en kombination i taget. Om resultatet är bättre än det bästa du har hittat hittills så sparar du informationen om den bättre kombinationen och kasserar det tidigare resultatet. Är resultatet sämre så går du bara vidare utan att spara någonting.
Beräkna alltså en kombination i taget. Om resultatet är bättre än det bästa du har hittat hittills så sparar du informationen om den bättre kombinationen och kasserar det tidigare resultatet. Är resultatet sämre så går du bara vidare utan att spara någonting.
Re: Optimering i Python med E-serier
Tack, det ska jag såklart testa. Snöade in på numpy och då blev mina beräkningar mer matris/Matlab-inspirerade men så behöver det såklart inte vara. Sedan spelar det inte någon större roll om optimeringen skulle ta lång tid.
Re: Optimering i Python med E-serier
Nu har jag knåpat ihop något som jag tror ska fungera. För att få upp hastigheten omvandlas sympy uttryck till en vanlig funktion.
Kod: Markera allt
# -*- coding: utf-8 -*-
"""
Add description!
"""
import sympy as sp
class BestCombinations():
def __init__(self, expression, variables, series, goal):
print("In init of model/BestCombinations")
print(f" Number of variables: {len(variables)}")
print(f" Number of series: {len(series)}")
y = sp.lambdify(variables, expression)
min_error = float("inf")
if len(variables) == 2:
x1, x2 = series
print(f" len(x1) = {len(x1)}")
print(f" len(x2) = {len(x2)}")
n1 = 0
while n1 < len(x1):
n2 = 0
while n2 < len(x2):
error = abs(goal - y(x1[n1], x2[n2]))
if error < min_error:
min_error = error
self.results = [x1[n1], x2[n2]]
print(f"Run {(n1+1)*(n2+1)} of {len(x1)*len(x2)}")
print(f" {self.results}")
print(f" min_error = {min_error}")
elif error == min_error:
self.results = self.results, [x1[n1], x2[n2]]
print(f"Run {(n1+1)*(n2+1)} of {len(x1)*len(x2)}")
print(f" {self.results}")
print(f" min_error = {min_error}")
n2 += 1
n1 += 1
else:
print("ERROR in init of model/BestCombinations")
Kod: Markera allt
# -*- coding: utf-8 -*-
"""
Add description!
"""
import sympy as sp
import numpy as np
from model.bestcombinations import BestCombinations
"""
Series connection with two resistors
"""
R, R1, R2 = sp.symbols('R R1 R2')
R = R1 + R2
ser1 = np.array([0, 1, 1.5, 2.2, 3.3, 4.7, 6.8])
ser1 = np.append(ser1, 10*ser1)
ser1 = np.append(ser1, 100*ser1)
ser2 = np.array([0, 1, 2.2, 4.7])
ser2 = np.append(ser2, 10*ser2)
ser2 = np.append(ser2, 100*ser2)
seriesResistorsCombinations = BestCombinations(
R, [R1, R2], [ser1, ser2], 56)
print(f"""seriesResistorsCombinations.results =
{seriesResistorsCombinations.results}""")
Re: Optimering i Python med E-serier
Bra att du fått ihop en lösning.
Passar på att lämna lite "lösa" kommentarer.
Det är god praxis att i huvudprogrammet wrappa alla kod i en metod, kalla den vad du vill och sedan använda:
Anledningen är att alla som använder din python-modul och importerar den filen automagiskt kommer få sidoeffekten att alla kod faktiskt exekveras. Vilket kanske inte är önskvärt.
Vidare så fundera noga om du verkligen behöver wrappa din modell i en klass.
I python är det lätt gjort och ofta landar man i att man skapar "fusk-objekt" mest beståendes av metoder.
Använder man klasser så ska det finnas en poäng med det och inte bara rakt av exekvera en massa kod i initieringen av en klass.
I detta exempel (som mycket väl kan vara väldigt kondenserat) så skulle jag starkt överväga att bara ha en metod likt:
När jag ändå nosar på moduler, klasser etc. så är python trevlig då det finns så mycket färdigt att använda. Man bör överväga vad "kostnaden" är att ta in en dependency (storlek, maintenance, uppdateringar etc.) mot nyttan.
Just i det exempel du postar så förstår jag att du började från en numpy-lösning som var minnesintensiv till att sedan göra en mer iterativ lösning. Vidare har du använt sympy för att på ett smidigt sätt uttrycka ekvationer. Dessa använder ca 14MB respektive 6MB och de används tämligen sparsmakat. Jag hade övervägt att klara mig utan dessa.
När du jobbar med enklare typer av formler så kan du enkelt definiera dom som vanliga python-metoder (eller lambda-metoder).
En till punkt som man kan disktuera är iterationer i python. Ofta när man ser.
eller
så är det i 99 fall av 100 pga vilken bakrund man har. Det är (nästan) alltid renare och enklare att följa med då man låter python få iterera över objekt likt:
om man behöver ha ut ett index samtidigt för status eller vad som helst så överväg att istället använda enumerate() på iteratorn:
Så till sist en hastigt omskriven version (utan utskrifter etc. för att fokusera mer på koden
Hoppas du fortsatt gillar python och att du fortsätter att lösa spännande problem
PS. problemet du löser är en variant på "Coin Change Problem" som man klassiskt löser med dynamisk programmering. En fördel med att göra denna uttömmande sökning är att du hittar alla lösningar och att du kan knyta vikter/kostnader till varje motstånd och för varje lösning räkna ut den "bästa" (den som använder billigast motstånd, den som använder motstånd som finns på lager, den som använder motstånd som det finns gott om på lager, etc.... möjligheterna är "oändliga").
DS
Passar på att lämna lite "lösa" kommentarer.
Det är god praxis att i huvudprogrammet wrappa alla kod i en metod, kalla den vad du vill och sedan använda:
Kod: Markera allt
if __name__ == "__main__":
det_namn_du_satte()
Vidare så fundera noga om du verkligen behöver wrappa din modell i en klass.
I python är det lätt gjort och ofta landar man i att man skapar "fusk-objekt" mest beståendes av metoder.
Använder man klasser så ska det finnas en poäng med det och inte bara rakt av exekvera en massa kod i initieringen av en klass.
I detta exempel (som mycket väl kan vara väldigt kondenserat) så skulle jag starkt överväga att bara ha en metod likt:
Kod: Markera allt
def find_optimal_component_values(expression, variables, series, goal):
...
Just i det exempel du postar så förstår jag att du började från en numpy-lösning som var minnesintensiv till att sedan göra en mer iterativ lösning. Vidare har du använt sympy för att på ett smidigt sätt uttrycka ekvationer. Dessa använder ca 14MB respektive 6MB och de används tämligen sparsmakat. Jag hade övervägt att klara mig utan dessa.
När du jobbar med enklare typer av formler så kan du enkelt definiera dom som vanliga python-metoder (eller lambda-metoder).
En till punkt som man kan disktuera är iterationer i python. Ofta när man ser.
Kod: Markera allt
for i in range(len(some_list)):
do_stuff(some_list[i])
Kod: Markera allt
i = 0
while i < len(range(some_list)):
do_stuff(some_list[i])
i += 1
Kod: Markera allt
for value in some_list:
do_stuff(value)
Kod: Markera allt
for index, value in enumerate(some_list):
print(f"working on {index}")
do_stuff(value)
Kod: Markera allt
from functools import reduce
from itertools import product
def calculate_best(expression, series: list, goal: float):
min_error = float("inf")
results = set()
if len(series) != 2:
print("ERROR in init of model/BestCombinations")
return
for x1, x2 in product(*series):
error = abs(goal - expression(x1, x2))
if x2 < x1:
x1, x2 = x2, x1
if error < min_error:
min_error = error
results = {(x1, x2)}
elif error == min_error:
results.add((x1, x2))
return results
def main():
def expression(x1: float, x2: float):
return x1+x2
def add_multiples(s: set, n: float):
s.update(list(map(lambda v: v*n, ser1)))
ser1 = set((0, 1, 1.5, 2.2, 3.3, 4.7, 6.8))
add_multiples(ser1, 10)
add_multiples(ser1, 100)
ser2 = set((0, 1, 2.2, 4.7))
add_multiples(ser2, 10)
add_multiples(ser2, 100)
result = calculate_best(expression, [ser1, ser2], 56)
print(f"{result=}")
if __name__ == "__main__":
main()
Hoppas du fortsatt gillar python och att du fortsätter att lösa spännande problem
PS. problemet du löser är en variant på "Coin Change Problem" som man klassiskt löser med dynamisk programmering. En fördel med att göra denna uttömmande sökning är att du hittar alla lösningar och att du kan knyta vikter/kostnader till varje motstånd och för varje lösning räkna ut den "bästa" (den som använder billigast motstånd, den som använder motstånd som finns på lager, den som använder motstånd som det finns gott om på lager, etc.... möjligheterna är "oändliga").
DS