agroplan-ai-cli 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,248 @@
1
+ """
2
+ Validador por força bruta para comparar com o Algoritmo Genético
3
+ """
4
+
5
+ import itertools
6
+ from core.terrain_analyzer import analisar_todos_talhoes
7
+ from core.genetic_optimizer import criar_funcao_fitness
8
+
9
+
10
+ def validar_por_forca_bruta(culturas, talhoes, regras, objetivo='equilibrado'):
11
+ """
12
+ Testa todas as combinações possíveis de culturas por talhão
13
+
14
+ Útil para validar o AG em conjuntos pequenos de dados.
15
+
16
+ Args:
17
+ culturas: DataFrame com culturas
18
+ talhoes: DataFrame com talhões
19
+ regras: DataFrame com regras
20
+ objetivo: 'equilibrado', 'lucro', 'risco' ou 'sustentavel'
21
+
22
+ Returns:
23
+ Dicionário com melhor solução encontrada
24
+ """
25
+ # Analisa todos os talhões
26
+ analises = analisar_todos_talhoes(talhoes, culturas, regras)
27
+
28
+ # Cria função fitness
29
+ fitness_function = criar_funcao_fitness(analises, culturas, objetivo)
30
+
31
+ # Gera todas as combinações possíveis
32
+ # Cada talhão pode ter qualquer cultura do seu ranking
33
+ espacos_genes = []
34
+ for analise in analises:
35
+ num_culturas = len(analise['ranking'])
36
+ espacos_genes.append(range(num_culturas))
37
+
38
+ # Calcula total de combinações
39
+ total_combinacoes = 1
40
+ for espaco in espacos_genes:
41
+ total_combinacoes *= len(espaco)
42
+
43
+ # Se for muito grande, retorna aviso
44
+ if total_combinacoes > 10000:
45
+ return {
46
+ 'erro': True,
47
+ 'mensagem': f'Número de combinações muito grande ({total_combinacoes}). Força bruta não é viável.',
48
+ 'total_combinacoes': total_combinacoes
49
+ }
50
+
51
+ # Testa todas as combinações
52
+ melhor_solucao = None
53
+ melhor_fitness = -float('inf')
54
+ melhor_plano = None
55
+
56
+ for combinacao in itertools.product(*espacos_genes):
57
+ # Calcula fitness desta combinação
58
+ fitness = fitness_function(None, list(combinacao), 0)
59
+
60
+ if fitness > melhor_fitness:
61
+ melhor_fitness = fitness
62
+ melhor_solucao = list(combinacao)
63
+
64
+ # Constrói o plano da melhor solução
65
+ plano = []
66
+ lucro_total = 0
67
+ area_total = 0
68
+ risco_ponderado = 0
69
+ culturas_usadas = set()
70
+
71
+ for i, gene in enumerate(melhor_solucao):
72
+ analise = analises[i]
73
+ talhao = analise['talhao']
74
+ ranking = analise['ranking']
75
+
76
+ cultura_idx = int(gene) % len(ranking)
77
+ cultura = ranking[cultura_idx]
78
+
79
+ plano.append({
80
+ 'talhao': talhao['id'],
81
+ 'area': talhao['area'],
82
+ 'solo': talhao['solo'],
83
+ 'clima': talhao['clima'],
84
+ 'relevo': talhao['relevo'],
85
+ 'agua': talhao['agua'],
86
+ 'cultura': cultura['cultura'],
87
+ 'lucro_estimado': cultura['lucro_total'],
88
+ 'risco': cultura['risco'],
89
+ 'nota': cultura['nota'],
90
+ 'tempo': cultura['tempo']
91
+ })
92
+
93
+ lucro_total += cultura['lucro_total']
94
+ area_total += talhao['area']
95
+ risco_ponderado += cultura['risco'] * talhao['area']
96
+ culturas_usadas.add(cultura['cultura'])
97
+
98
+ risco_medio = risco_ponderado / area_total if area_total > 0 else 0
99
+
100
+ return {
101
+ 'erro': False,
102
+ 'plano': plano,
103
+ 'melhor_fitness': melhor_fitness,
104
+ 'total_combinacoes': total_combinacoes,
105
+ 'lucro_total': lucro_total,
106
+ 'risco_medio': risco_medio,
107
+ 'area_total': area_total,
108
+ 'diversidade': len(culturas_usadas),
109
+ 'objetivo': objetivo,
110
+ 'solucao': melhor_solucao
111
+ }
112
+
113
+
114
+ def comparar_ag_com_forca_bruta(culturas, talhoes, regras, objetivo='equilibrado', seed=42):
115
+ """
116
+ Compara resultado do AG com força bruta
117
+
118
+ Args:
119
+ culturas: DataFrame com culturas
120
+ talhoes: DataFrame com talhões
121
+ regras: DataFrame com regras
122
+ objetivo: objetivo do AG
123
+ seed: seed para reprodutibilidade do AG
124
+
125
+ Returns:
126
+ Dicionário com comparação
127
+ """
128
+ from core.genetic_optimizer import otimizar_plano_genetico
129
+
130
+ # Executa força bruta
131
+ print(" Testando todas as combinações possíveis...")
132
+ resultado_fb = validar_por_forca_bruta(culturas, talhoes, regras, objetivo)
133
+
134
+ if resultado_fb.get('erro'):
135
+ return resultado_fb
136
+
137
+ # Executa AG
138
+ print(" Executando Algoritmo Genético...")
139
+ resultado_ag = otimizar_plano_genetico(culturas, talhoes, regras, objetivo, seed=seed)
140
+
141
+ # Compara resultados
142
+ ag_encontrou_otimo = abs(resultado_ag['fitness'] - resultado_fb['melhor_fitness']) < 0.01
143
+ diferenca_fitness = resultado_ag['fitness'] - resultado_fb['melhor_fitness']
144
+ diferenca_lucro = resultado_ag['lucro_total'] - resultado_fb['lucro_total']
145
+
146
+ # Gera análise
147
+ if ag_encontrou_otimo:
148
+ analise = (
149
+ "✅ O Algoritmo Genético encontrou o ótimo global neste conjunto de dados. "
150
+ f"Como o conjunto atual possui apenas {resultado_fb['total_combinacoes']} combinações, "
151
+ "a força bruta ainda é viável. Porém, em cenários maiores, como 10 talhões e 8 culturas, "
152
+ "seriam mais de 1 bilhão de combinações, tornando o Algoritmo Genético essencial."
153
+ )
154
+ else:
155
+ percentual = (diferenca_fitness / resultado_fb['melhor_fitness']) * 100 if resultado_fb['melhor_fitness'] > 0 else 0
156
+ analise = (
157
+ f"⚠️ O Algoritmo Genético encontrou uma solução {abs(percentual):.1f}% "
158
+ f"{'melhor' if diferenca_fitness > 0 else 'pior'} que o ótimo global. "
159
+ f"Diferença de fitness: {diferenca_fitness:.2f}. "
160
+ "Isso pode ocorrer devido à aleatoriedade do AG. "
161
+ "Execute múltiplas rodadas para avaliar a estabilidade."
162
+ )
163
+
164
+ return {
165
+ 'erro': False,
166
+ 'ag': resultado_ag,
167
+ 'forca_bruta': resultado_fb,
168
+ 'ag_encontrou_otimo_global': ag_encontrou_otimo,
169
+ 'diferenca_fitness': diferenca_fitness,
170
+ 'diferenca_lucro': diferenca_lucro,
171
+ 'analise': analise
172
+ }
173
+
174
+
175
+ def executar_multiplas_rodadas(culturas, talhoes, regras, objetivo='equilibrado', rodadas=10):
176
+ """
177
+ Executa o AG múltiplas vezes para avaliar estabilidade
178
+
179
+ Args:
180
+ culturas: DataFrame com culturas
181
+ talhoes: DataFrame com talhões
182
+ regras: DataFrame com regras
183
+ objetivo: objetivo do AG
184
+ rodadas: número de execuções
185
+
186
+ Returns:
187
+ Dicionário com estatísticas
188
+ """
189
+ from core.genetic_optimizer import otimizar_plano_genetico
190
+ import statistics
191
+
192
+ resultados = []
193
+ fitness_list = []
194
+ lucros_list = []
195
+ riscos_list = []
196
+
197
+ print(f" Executando {rodadas} rodadas do Algoritmo Genético...")
198
+
199
+ for i in range(rodadas):
200
+ # Usa seed diferente para cada rodada
201
+ resultado = otimizar_plano_genetico(culturas, talhoes, regras, objetivo, seed=i)
202
+ resultados.append(resultado)
203
+ fitness_list.append(resultado['fitness'])
204
+ lucros_list.append(resultado['lucro_total'])
205
+ riscos_list.append(resultado['risco_medio'])
206
+ print(f" Rodada {i+1}/{rodadas}: Fitness = {resultado['fitness']:.2f}")
207
+
208
+ # Encontra melhor e pior
209
+ melhor_idx = fitness_list.index(max(fitness_list))
210
+ pior_idx = fitness_list.index(min(fitness_list))
211
+
212
+ melhor_resultado = resultados[melhor_idx]
213
+ pior_resultado = resultados[pior_idx]
214
+
215
+ # Calcula estatísticas
216
+ fitness_medio = statistics.mean(fitness_list)
217
+ fitness_desvio = statistics.stdev(fitness_list) if len(fitness_list) > 1 else 0
218
+ lucro_medio = statistics.mean(lucros_list)
219
+ risco_medio = statistics.mean(riscos_list)
220
+
221
+ # Avalia estabilidade
222
+ coef_variacao = (fitness_desvio / fitness_medio * 100) if fitness_medio > 0 else 0
223
+
224
+ if coef_variacao < 2:
225
+ estabilidade = 'alta'
226
+ estabilidade_desc = 'O algoritmo apresentou alta estabilidade, encontrando soluções muito semelhantes em todas as execuções.'
227
+ elif coef_variacao < 5:
228
+ estabilidade = 'média'
229
+ estabilidade_desc = 'O algoritmo apresentou estabilidade média, com alguma variação entre as execuções.'
230
+ else:
231
+ estabilidade = 'baixa'
232
+ estabilidade_desc = 'O algoritmo apresentou variação significativa entre as execuções. Considere aumentar o número de gerações ou população.'
233
+
234
+ return {
235
+ 'rodadas': rodadas,
236
+ 'melhor_fitness': max(fitness_list),
237
+ 'fitness_medio': fitness_medio,
238
+ 'pior_fitness': min(fitness_list),
239
+ 'desvio_padrao': fitness_desvio,
240
+ 'coeficiente_variacao': coef_variacao,
241
+ 'lucro_medio': lucro_medio,
242
+ 'risco_medio': risco_medio,
243
+ 'melhor_plano': melhor_resultado,
244
+ 'pior_plano': pior_resultado,
245
+ 'estabilidade': estabilidade,
246
+ 'estabilidade_descricao': estabilidade_desc,
247
+ 'todos_resultados': resultados
248
+ }
@@ -0,0 +1,328 @@
1
+ """
2
+ Otimizador de plantio usando Algoritmo Genético (PyGAD)
3
+ """
4
+
5
+ import pygad
6
+ import numpy as np
7
+ from core.terrain_analyzer import analisar_todos_talhoes
8
+ from core.scorer import calcular_lucro_por_hectare
9
+
10
+
11
+ # Configurações de pesos por objetivo
12
+ PESOS_OBJETIVOS = {
13
+ 'equilibrado': {
14
+ 'lucro': 0.40,
15
+ 'compatibilidade': 0.30,
16
+ 'seguranca': 0.20,
17
+ 'diversidade': 0.10
18
+ },
19
+ 'lucro': {
20
+ 'lucro': 0.60,
21
+ 'compatibilidade': 0.20,
22
+ 'seguranca': 0.10,
23
+ 'diversidade': 0.10
24
+ },
25
+ 'risco': {
26
+ 'lucro': 0.20,
27
+ 'compatibilidade': 0.30,
28
+ 'seguranca': 0.40,
29
+ 'diversidade': 0.10
30
+ },
31
+ 'sustentavel': {
32
+ 'lucro': 0.20,
33
+ 'compatibilidade': 0.40,
34
+ 'seguranca': 0.20,
35
+ 'diversidade': 0.20
36
+ }
37
+ }
38
+
39
+
40
+ def criar_funcao_fitness(analises, culturas, objetivo='equilibrado'):
41
+ """
42
+ Cria a função fitness para o Algoritmo Genético
43
+
44
+ A função avalia cada solução (combinação de culturas) considerando:
45
+ - Lucro total
46
+ - Compatibilidade com terreno
47
+ - Risco
48
+ - Diversidade de culturas
49
+ - Penalidades agronômicas
50
+ """
51
+ pesos = PESOS_OBJETIVOS.get(objetivo, PESOS_OBJETIVOS['equilibrado'])
52
+
53
+ # Calcula valores máximos para normalização
54
+ todos_lucros = []
55
+ todas_notas = []
56
+
57
+ for analise in analises:
58
+ for cultura in analise['ranking']:
59
+ todos_lucros.append(cultura['lucro_total'])
60
+ todas_notas.append(cultura['nota'])
61
+
62
+ lucro_max = max(todos_lucros) if todos_lucros else 1
63
+ lucro_min = min(todos_lucros) if todos_lucros else 0
64
+ nota_max = max(todas_notas) if todas_notas else 1
65
+
66
+ def fitness_func(ga_instance, solution, solution_idx):
67
+ """
68
+ Calcula fitness de uma solução
69
+
70
+ solution: array de índices de culturas, ex: [0, 1, 1]
71
+ """
72
+ lucro_total = 0
73
+ notas_total = 0
74
+ area_total = 0
75
+ risco_ponderado = 0
76
+ culturas_usadas = set()
77
+ penalidades = 0
78
+
79
+ # Avalia cada talhão
80
+ for i, gene in enumerate(solution):
81
+ analise = analises[i]
82
+ talhao = analise['talhao']
83
+ ranking = analise['ranking']
84
+
85
+ # Gene representa índice da cultura no ranking
86
+ cultura_idx = int(gene) % len(ranking)
87
+ cultura = ranking[cultura_idx]
88
+
89
+ # Acumula métricas
90
+ lucro_total += cultura['lucro_total']
91
+ notas_total += cultura['nota']
92
+ area_total += talhao['area']
93
+ risco_ponderado += cultura['risco'] * talhao['area']
94
+ culturas_usadas.add(cultura['cultura'])
95
+
96
+ # PENALIDADES
97
+
98
+ # 1. Compatibilidade geral baixa (nota < 60)
99
+ if cultura['nota'] < 60:
100
+ penalidades += 15
101
+
102
+ # 2. Água insuficiente (alta necessidade + baixa disponibilidade)
103
+ if cultura['compatibilidade_agua'] == 0:
104
+ penalidades += 20
105
+
106
+ # 3. Solo incompatível
107
+ if cultura['compatibilidade_solo'] == 0:
108
+ penalidades += 10
109
+
110
+ # 4. Clima incompatível
111
+ if cultura['compatibilidade_clima'] == 0:
112
+ penalidades += 10
113
+
114
+ # Calcula métricas agregadas
115
+ risco_medio = risco_ponderado / area_total if area_total > 0 else 0
116
+ nota_media = notas_total / len(solution) if len(solution) > 0 else 0
117
+
118
+ # PENALIDADES GLOBAIS
119
+
120
+ # 5. Monocultura (mesma cultura em todos os talhões)
121
+ if len(culturas_usadas) == 1:
122
+ penalidades += 25
123
+
124
+ # 6. Risco muito alto (> 45%)
125
+ if risco_medio > 45:
126
+ penalidades += 20
127
+
128
+ # 7. Nota média muito baixa (< 70)
129
+ if nota_media < 70:
130
+ penalidades += 10
131
+
132
+ # NORMALIZAÇÃO DOS COMPONENTES (todos entre 0-100)
133
+
134
+ # Lucro normalizado (0-100)
135
+ if lucro_max > lucro_min:
136
+ lucro_normalizado = min(100 * (lucro_total - lucro_min) / (lucro_max - lucro_min), 100)
137
+ else:
138
+ lucro_normalizado = 50
139
+
140
+ # Compatibilidade normalizada (0-100)
141
+ compatibilidade_normalizada = min((nota_media / nota_max) * 100 if nota_max > 0 else 50, 100)
142
+
143
+ # Segurança (inverso do risco, 0-100)
144
+ seguranca = min(max(0, 100 - risco_medio), 100)
145
+
146
+ # Diversidade (0-100)
147
+ max_diversidade = len(solution)
148
+ diversidade = min((len(culturas_usadas) / max_diversidade) * 100 if max_diversidade > 0 else 0, 100)
149
+
150
+ # CÁLCULO DO FITNESS
151
+ fitness = (
152
+ lucro_normalizado * pesos['lucro'] +
153
+ compatibilidade_normalizada * pesos['compatibilidade'] +
154
+ seguranca * pesos['seguranca'] +
155
+ diversidade * pesos['diversidade']
156
+ ) - penalidades
157
+
158
+ return max(0, fitness) # Garante que fitness não seja negativo
159
+
160
+ return fitness_func
161
+
162
+
163
+ def otimizar_plano_genetico(culturas, talhoes, regras, objetivo='equilibrado', geracoes=100, populacao=50, seed=None):
164
+ """
165
+ Otimiza o plano de plantio usando Algoritmo Genético
166
+
167
+ Args:
168
+ culturas: DataFrame com culturas disponíveis
169
+ talhoes: DataFrame com talhões
170
+ regras: DataFrame com regras de cultivo
171
+ objetivo: 'equilibrado', 'lucro', 'risco' ou 'sustentavel'
172
+ geracoes: número de gerações do AG
173
+ populacao: tamanho da população
174
+ seed: seed para reprodutibilidade (opcional)
175
+
176
+ Returns:
177
+ Dicionário com plano otimizado e métricas
178
+ """
179
+ # Analisa todos os talhões
180
+ analises = analisar_todos_talhoes(talhoes, culturas, regras)
181
+
182
+ # Número de genes = número de talhões
183
+ num_genes = len(analises)
184
+
185
+ # Cada gene pode ter valor de 0 até (número de culturas - 1)
186
+ # Usamos o tamanho do ranking como limite
187
+ gene_space = []
188
+ for analise in analises:
189
+ num_culturas = len(analise['ranking'])
190
+ gene_space.append(range(num_culturas))
191
+
192
+ # Cria função fitness
193
+ fitness_function = criar_funcao_fitness(analises, culturas, objetivo)
194
+
195
+ # Define seed se fornecida
196
+ random_seed = seed if seed is not None else None
197
+
198
+ # Lista para armazenar histórico de fitness
199
+ historico_fitness = []
200
+
201
+ def on_generation(ga_instance):
202
+ """Callback chamado a cada geração para salvar histórico"""
203
+ geracao = ga_instance.generations_completed
204
+ melhor_fitness = ga_instance.best_solution()[1]
205
+ fitness_medio = np.mean(ga_instance.last_generation_fitness)
206
+ historico_fitness.append({
207
+ 'geracao': geracao,
208
+ 'melhor_fitness': melhor_fitness,
209
+ 'fitness_medio': fitness_medio
210
+ })
211
+
212
+ # Configura o Algoritmo Genético
213
+ ga_instance = pygad.GA(
214
+ num_generations=geracoes,
215
+ num_parents_mating=max(2, populacao // 4),
216
+ fitness_func=fitness_function,
217
+ sol_per_pop=populacao,
218
+ num_genes=num_genes,
219
+ gene_space=gene_space,
220
+ gene_type=int,
221
+ parent_selection_type="sss", # Steady-state selection
222
+ keep_parents=2,
223
+ crossover_type="single_point",
224
+ mutation_type="random",
225
+ mutation_percent_genes=20,
226
+ random_seed=random_seed,
227
+ on_generation=on_generation
228
+ )
229
+
230
+ # Executa o AG
231
+ ga_instance.run()
232
+
233
+ # Obtém a melhor solução
234
+ solution, solution_fitness, solution_idx = ga_instance.best_solution()
235
+
236
+ # Constrói o plano a partir da solução
237
+ plano = []
238
+ lucro_total = 0
239
+ area_total = 0
240
+ risco_ponderado = 0
241
+ culturas_usadas = set()
242
+
243
+ for i, gene in enumerate(solution):
244
+ analise = analises[i]
245
+ talhao = analise['talhao']
246
+ ranking = analise['ranking']
247
+
248
+ cultura_idx = int(gene) % len(ranking)
249
+ cultura = ranking[cultura_idx]
250
+
251
+ plano.append({
252
+ 'talhao': talhao['id'],
253
+ 'area': talhao['area'],
254
+ 'solo': talhao['solo'],
255
+ 'clima': talhao['clima'],
256
+ 'relevo': talhao['relevo'],
257
+ 'agua': talhao['agua'],
258
+ 'cultura': cultura['cultura'],
259
+ 'lucro_estimado': cultura['lucro_total'],
260
+ 'risco': cultura['risco'],
261
+ 'nota': cultura['nota'],
262
+ 'tempo': cultura['tempo']
263
+ })
264
+
265
+ lucro_total += cultura['lucro_total']
266
+ area_total += talhao['area']
267
+ risco_ponderado += cultura['risco'] * talhao['area']
268
+ culturas_usadas.add(cultura['cultura'])
269
+
270
+ risco_medio = risco_ponderado / area_total if area_total > 0 else 0
271
+
272
+ # Gera justificativa
273
+ justificativa = gerar_justificativa_ag(objetivo, lucro_total, risco_medio, len(culturas_usadas), solution_fitness)
274
+
275
+ return {
276
+ 'plano': plano,
277
+ 'lucro_total': lucro_total,
278
+ 'risco_medio': risco_medio,
279
+ 'area_total': area_total,
280
+ 'fitness': solution_fitness,
281
+ 'geracoes': geracoes,
282
+ 'objetivo': objetivo,
283
+ 'diversidade': len(culturas_usadas),
284
+ 'justificativa': justificativa,
285
+ 'historico_fitness': historico_fitness,
286
+ 'seed': seed
287
+ }
288
+
289
+
290
+ def gerar_justificativa_ag(objetivo, lucro, risco, diversidade, fitness):
291
+ """Gera justificativa para o plano otimizado pelo AG"""
292
+
293
+ justificativas_base = {
294
+ 'equilibrado': (
295
+ f"O algoritmo genético encontrou um plano equilibrado entre lucro, risco e "
296
+ f"compatibilidade do terreno. A solução mantém alto retorno financeiro "
297
+ f"(R$ {lucro:,.2f}) sem ultrapassar níveis críticos de risco ({risco:.1f}%). "
298
+ ),
299
+ 'lucro': (
300
+ f"O algoritmo genético priorizou maximização de lucro, encontrando uma solução "
301
+ f"com retorno de R$ {lucro:,.2f}. O risco médio de {risco:.1f}% foi considerado "
302
+ f"aceitável para este objetivo. "
303
+ ),
304
+ 'risco': (
305
+ f"O algoritmo genético priorizou segurança, mantendo o risco médio em apenas "
306
+ f"{risco:.1f}%. O lucro de R$ {lucro:,.2f} foi otimizado dentro das restrições "
307
+ f"de baixo risco. "
308
+ ),
309
+ 'sustentavel': (
310
+ f"O algoritmo genético priorizou compatibilidade ambiental e uso eficiente de "
311
+ f"recursos. A solução alcançou lucro de R$ {lucro:,.2f} com risco de {risco:.1f}%, "
312
+ f"mantendo alta compatibilidade com as características dos talhões. "
313
+ )
314
+ }
315
+
316
+ justificativa = justificativas_base.get(objetivo, justificativas_base['equilibrado'])
317
+
318
+ # Adiciona informação sobre diversidade
319
+ if diversidade == 1:
320
+ justificativa += "Nota: O plano utiliza apenas uma cultura (monocultura)."
321
+ elif diversidade == 2:
322
+ justificativa += f"O plano utiliza {diversidade} culturas diferentes, oferecendo alguma diversificação."
323
+ else:
324
+ justificativa += f"O plano utiliza {diversidade} culturas diferentes, oferecendo boa diversificação."
325
+
326
+ justificativa += f" Fitness final: {fitness:.2f}."
327
+
328
+ return justificativa
@@ -0,0 +1,8 @@
1
+ import pandas as pd
2
+
3
+ def carregar_dados():
4
+ """Carrega os dados de culturas, talhões e regras dos arquivos CSV"""
5
+ culturas = pd.read_csv("data/culturas.csv")
6
+ talhoes = pd.read_csv("data/talhoes.csv")
7
+ regras = pd.read_csv("data/regras_culturas.csv")
8
+ return culturas, talhoes, regras
@@ -0,0 +1,79 @@
1
+ from core.terrain_analyzer import analisar_todos_talhoes
2
+ from core.scenario_simulator import simular_cenarios
3
+ from core.genetic_optimizer import otimizar_plano_genetico
4
+
5
+ def gerar_plano_inteligente(culturas, talhoes, regras):
6
+ """
7
+ Gera um plano de plantio inteligente usando análise de terreno
8
+
9
+ Em vez de escolher culturas aleatoriamente, analisa as características
10
+ de cada talhão e recomenda a cultura mais adequada com base em:
11
+ - Compatibilidade de solo
12
+ - Compatibilidade de clima
13
+ - Compatibilidade de relevo
14
+ - Disponibilidade de água
15
+ - Lucro estimado
16
+ - Risco
17
+ """
18
+ # Analisa todos os talhões
19
+ analises = analisar_todos_talhoes(talhoes, culturas, regras)
20
+
21
+ plano = []
22
+
23
+ for analise in analises:
24
+ talhao = analise['talhao']
25
+ melhor = analise['melhor_cultura']
26
+ ranking = analise['ranking']
27
+ justificativa = analise['justificativa']
28
+
29
+ if melhor:
30
+ plano.append({
31
+ 'talhao': talhao['id'],
32
+ 'area': talhao['area'],
33
+ 'solo': talhao['solo'],
34
+ 'clima': talhao['clima'],
35
+ 'relevo': talhao['relevo'],
36
+ 'agua': talhao['agua'],
37
+ 'cultura_recomendada': melhor['cultura'],
38
+ 'nota': melhor['nota'],
39
+ 'lucro_estimado': melhor['lucro_total'],
40
+ 'risco': melhor['risco'],
41
+ 'tempo': melhor['tempo'],
42
+ 'ranking': ranking,
43
+ 'justificativa': justificativa
44
+ })
45
+
46
+ return plano
47
+
48
+
49
+ def gerar_cenarios(culturas, talhoes, regras):
50
+ """
51
+ Gera múltiplos cenários de planejamento para comparação
52
+
53
+ Retorna diferentes estratégias:
54
+ - Equilibrado
55
+ - Máximo Lucro
56
+ - Baixo Risco
57
+ - Sustentável
58
+ - Conservador
59
+ """
60
+ return simular_cenarios(culturas, talhoes, regras)
61
+
62
+
63
+ def gerar_plano_genetico(culturas, talhoes, regras, objetivo='equilibrado', geracoes=100, populacao=50, seed=None):
64
+ """
65
+ Gera plano otimizado usando Algoritmo Genético
66
+
67
+ Args:
68
+ culturas: DataFrame com culturas
69
+ talhoes: DataFrame com talhões
70
+ regras: DataFrame com regras
71
+ objetivo: 'equilibrado', 'lucro', 'risco' ou 'sustentavel'
72
+ geracoes: número de gerações do AG
73
+ populacao: tamanho da população
74
+ seed: seed para reprodutibilidade
75
+
76
+ Returns:
77
+ Dicionário com plano otimizado
78
+ """
79
+ return otimizar_plano_genetico(culturas, talhoes, regras, objetivo, geracoes, populacao, seed)