classiq 0.33.0__py3-none-any.whl → 0.34.0__py3-none-any.whl

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.
Files changed (35) hide show
  1. classiq/_internals/api_wrapper.py +9 -20
  2. classiq/_internals/jobs.py +9 -2
  3. classiq/executor.py +3 -10
  4. classiq/interface/_version.py +1 -1
  5. classiq/interface/backend/backend_preferences.py +17 -0
  6. classiq/interface/backend/pydantic_backend.py +8 -0
  7. classiq/interface/backend/quantum_backend_providers.py +13 -1
  8. classiq/interface/chemistry/ground_state_problem.py +1 -1
  9. classiq/interface/chemistry/operator.py +198 -0
  10. classiq/interface/executor/execution_request.py +2 -12
  11. classiq/interface/generator/functions/core_lib_declarations/quantum_functions/atomic_quantum_functions.py +2 -2
  12. classiq/interface/generator/functions/core_lib_declarations/quantum_functions/std_lib_functions.py +20 -110
  13. classiq/interface/generator/generated_circuit.py +8 -44
  14. classiq/interface/generator/generated_circuit_data.py +2 -11
  15. classiq/interface/generator/model/preferences/preferences.py +2 -2
  16. classiq/interface/generator/quantum_function_call.py +1 -1
  17. classiq/interface/hardware.py +1 -0
  18. classiq/interface/ide/show.py +1 -14
  19. classiq/interface/model/quantum_function_call.py +9 -339
  20. classiq/interface/model/quantum_statement.py +3 -2
  21. classiq/interface/server/routes.py +5 -6
  22. classiq/model/function_handler.pyi +88 -88
  23. classiq/qmod/declaration_inferrer.py +34 -17
  24. classiq/qmod/model_state_container.py +6 -3
  25. classiq/qmod/qmod_builtins.py +892 -4
  26. classiq/qmod/qmod_parameter.py +24 -8
  27. classiq/qmod/quantum_expandable.py +6 -2
  28. classiq/qmod/quantum_function.py +9 -9
  29. {classiq-0.33.0.dist-info → classiq-0.34.0.dist-info}/METADATA +1 -1
  30. {classiq-0.33.0.dist-info → classiq-0.34.0.dist-info}/RECORD +31 -35
  31. classiq/interface/model/clients/__init__.py +0 -0
  32. classiq/interface/model/clients/qmod/__init__.py +0 -0
  33. classiq/interface/model/clients/qmod/qmod_builtins.py +0 -905
  34. classiq/interface/model/semantics.py +0 -15
  35. {classiq-0.33.0.dist-info → classiq-0.34.0.dist-info}/WHEEL +0 -0
@@ -1,59 +1,23 @@
1
- import functools
2
1
  import itertools
3
2
  import re
4
- from collections import defaultdict
5
- from typing import (
6
- TYPE_CHECKING,
7
- Any,
8
- Collection,
9
- Dict,
10
- Iterable,
11
- List,
12
- Mapping,
13
- Match,
14
- Optional,
15
- Sequence,
16
- Set,
17
- Tuple,
18
- Union,
19
- )
3
+ from typing import Any, Dict, List, Mapping, Optional, Sequence, Set, Union
20
4
 
21
5
  import pydantic
22
- from pydantic import BaseModel, Extra
6
+ from pydantic import BaseModel
23
7
 
24
- from classiq.interface.generator import function_param_list, function_params as f_params
25
8
  from classiq.interface.generator.arith.arithmetic import Arithmetic
26
9
  from classiq.interface.generator.control_state import ControlState
27
10
  from classiq.interface.generator.expressions.expression import Expression
28
- from classiq.interface.generator.function_params import (
29
- NAME_REGEX,
30
- FunctionParams,
31
- PortDirection,
32
- )
11
+ from classiq.interface.generator.function_params import NAME_REGEX
33
12
  from classiq.interface.generator.functions import FunctionDeclaration
34
13
  from classiq.interface.generator.functions.port_declaration import (
35
14
  PortDeclarationDirection,
36
15
  )
37
16
  from classiq.interface.generator.quantum_function_call import (
38
17
  BAD_CALL_NAME_ERROR_MSG,
39
- BAD_INPUT_ERROR_MSG,
40
- BAD_INPUT_EXPRESSION_MSG,
41
- BAD_INPUT_SLICING_MSG,
42
- BAD_OUTPUT_ERROR_MSG,
43
- BAD_OUTPUT_EXPRESSION_MSG,
44
- BAD_OUTPUT_SLICING_MSG,
45
- CUSTOM_FUNCTION_SINGLE_IO_ERROR,
46
- LEGAL_SLICING,
47
18
  SUFFIX_MARKER,
48
19
  randomize_suffix,
49
20
  )
50
- from classiq.interface.generator.slice_parsing_utils import (
51
- IO_REGEX,
52
- NAME,
53
- SLICING,
54
- parse_io_slicing,
55
- )
56
- from classiq.interface.generator.user_defined_function_params import CustomFunction
57
21
  from classiq.interface.helpers.custom_pydantic_types import PydanticNonEmptyString
58
22
  from classiq.interface.model.handle_binding import (
59
23
  HandleBinding,
@@ -67,7 +31,7 @@ from classiq.interface.model.quantum_function_declaration import (
67
31
  from classiq.interface.model.quantum_statement import QuantumOperation
68
32
  from classiq.interface.model.validation_handle import get_unique_handle_names
69
33
 
70
- from classiq.exceptions import ClassiqControlError, ClassiqValueError
34
+ from classiq.exceptions import ClassiqError, ClassiqValueError
71
35
 
72
36
 
73
37
  def _validate_no_duplicated_ports(
@@ -143,10 +107,6 @@ class QuantumFunctionCall(QuantumOperation):
143
107
  description="The function that is called"
144
108
  )
145
109
  params: Dict[str, Expression] = pydantic.Field(default_factory=dict)
146
- function_params: f_params.FunctionParams = pydantic.Field(
147
- description="The parameters necessary for defining the function",
148
- default_factory=CustomFunction,
149
- )
150
110
  strict_zero_ios: bool = pydantic.Field(
151
111
  default=True,
152
112
  description="Enables automated qubit allocation for pre-determined zero inputs "
@@ -196,7 +156,10 @@ class QuantumFunctionCall(QuantumOperation):
196
156
  )
197
157
 
198
158
  @property
199
- def func_decl(self) -> Optional[QuantumFunctionDeclaration]:
159
+ def func_decl(self) -> QuantumFunctionDeclaration:
160
+ if self._func_decl is None:
161
+ raise ClassiqError("Accessing an unresolved quantum function call")
162
+
200
163
  return self._func_decl
201
164
 
202
165
  def set_func_decl(self, fd: Optional[FunctionDeclaration]) -> None:
@@ -260,41 +223,13 @@ class QuantumFunctionCall(QuantumOperation):
260
223
  if isinstance(function, OperandIdentifier):
261
224
  function = function.name
262
225
 
263
- params = values.get("function_params")
264
- if isinstance(params, CustomFunction):
265
- if function == CustomFunction.discriminator() and params.name != "":
266
- function = params.name
267
-
268
226
  suffix = f"{SUFFIX_MARKER}_{randomize_suffix()}"
269
- if not function or params is None:
227
+ if not function:
270
228
  return name if name else suffix
271
229
  return f"{function}_{suffix}"
272
230
 
273
- @pydantic.root_validator(pre=True)
274
- def validate_composite_name(cls, values: Dict[str, Any]) -> Dict[str, Any]:
275
- if isinstance(values.get("unitary_params"), CustomFunction) and not values.get(
276
- "unitary"
277
- ):
278
- raise ClassiqValueError(
279
- "`PhaseEstimation` of a user define function (`CustomFunction`) must receive the function name from the `unitary` field"
280
- )
281
- return values
282
-
283
- @pydantic.root_validator(pre=True)
284
- def _parse_function_params(cls, values: Dict[str, Any]) -> Dict[str, Any]:
285
- f_params.parse_function_params_values(
286
- values=values,
287
- params_key="function_params",
288
- discriminator_key="function",
289
- param_classes=function_param_list.function_param_library.param_list,
290
- default_parser_class=CustomFunction,
291
- )
292
- return values
293
-
294
231
  @property
295
232
  def pos_param_args(self) -> Dict[str, Expression]:
296
- if TYPE_CHECKING:
297
- assert self.func_decl is not None
298
233
  return dict(
299
234
  zip(
300
235
  self.func_decl.param_decls.keys(),
@@ -308,8 +243,6 @@ class QuantumFunctionCall(QuantumOperation):
308
243
 
309
244
  @property
310
245
  def pos_operand_args(self) -> Dict[str, "QuantumOperand"]:
311
- if TYPE_CHECKING:
312
- assert self.func_decl is not None
313
246
  return dict(
314
247
  zip(
315
248
  self.func_decl.operand_declarations.keys(),
@@ -323,8 +256,6 @@ class QuantumFunctionCall(QuantumOperation):
323
256
 
324
257
  @property
325
258
  def pos_port_args(self) -> Dict[str, HandleBinding]:
326
- if TYPE_CHECKING:
327
- assert self.func_decl is not None
328
259
  return dict(
329
260
  zip(
330
261
  self.func_decl.port_declarations.keys(),
@@ -337,8 +268,6 @@ class QuantumFunctionCall(QuantumOperation):
337
268
  )
338
269
 
339
270
  def _update_pos_port_params(self) -> None:
340
- if TYPE_CHECKING:
341
- assert self.func_decl is not None
342
271
  for name, port_decl in self.func_decl.port_declarations.items():
343
272
  if port_decl.direction == PortDeclarationDirection.Input:
344
273
  self.inputs[name] = self.pos_port_args[name]
@@ -352,45 +281,10 @@ class QuantumFunctionCall(QuantumOperation):
352
281
  self.operands.update(self.pos_operand_args)
353
282
  self._update_pos_port_params()
354
283
 
355
- # TODO: note that this checks QuantumFunctionCall input register names
356
- # are PARTIAL to FunctionParams input register names, not EQUAL.
357
- # We might want to change that.
358
- @staticmethod
359
- def _validate_input_names(
360
- *,
361
- params: f_params.FunctionParams,
362
- input_names: Collection[str],
363
- control_states: List[ControlState],
364
- strict_zero_ios: bool,
365
- ) -> None:
366
- (
367
- invalid_expressions,
368
- invalid_slicings,
369
- invalid_names,
370
- ) = QuantumFunctionCall._get_invalid_ios(
371
- expressions=input_names,
372
- params=params,
373
- io=PortDirection.Input,
374
- control_states=control_states,
375
- strict_zero_ios=strict_zero_ios,
376
- )
377
- error_msg = []
378
- if invalid_expressions:
379
- error_msg.append(f"{BAD_INPUT_EXPRESSION_MSG}: {invalid_expressions}")
380
- if invalid_names:
381
- error_msg.append(f"{BAD_INPUT_ERROR_MSG}: {invalid_names}")
382
- if invalid_slicings:
383
- error_msg.append(f"{BAD_INPUT_SLICING_MSG}: {invalid_slicings}")
384
- if error_msg:
385
- raise ValueError("\n".join(error_msg))
386
-
387
284
  def resolve_function_decl(
388
285
  self,
389
286
  function_dict: Mapping[str, FunctionDeclaration],
390
287
  ) -> None:
391
- if not isinstance(self.function_params, CustomFunction):
392
- return
393
-
394
288
  if self._func_decl is None:
395
289
  func_decl = function_dict.get(self.func_name)
396
290
  if func_decl is None:
@@ -399,9 +293,6 @@ class QuantumFunctionCall(QuantumOperation):
399
293
  )
400
294
  self.set_func_decl(func_decl)
401
295
 
402
- if TYPE_CHECKING:
403
- assert self.func_decl is not None
404
-
405
296
  if self.positional_args:
406
297
  self._reduce_positional_args_to_keywords()
407
298
 
@@ -432,228 +323,10 @@ class QuantumFunctionCall(QuantumOperation):
432
323
  ), "when using the Arithmetic function, assign to the expression result register via the target parameter instead of the strict_zero_ios flag"
433
324
  return strict_zero_ios
434
325
 
435
- @pydantic.validator("control_states")
436
- def _validate_control_states(
437
- cls, control_states: List[ControlState], values: Dict[str, Any]
438
- ) -> List[ControlState]:
439
- control_names = [ctrl_state.name for ctrl_state in control_states]
440
- function_params = values.get("function_params")
441
- strict_zero_ios = values.get("strict_zero_ios")
442
- if not (
443
- isinstance(function_params, FunctionParams)
444
- and isinstance(strict_zero_ios, bool)
445
- ):
446
- return control_states
447
- all_input_names = [
448
- *function_params.inputs_full(strict_zero_ios=strict_zero_ios),
449
- *control_names,
450
- ]
451
- all_output_names = [*function_params.outputs, *control_names]
452
- if any(
453
- cls._has_repetitions(name_list)
454
- for name_list in (control_names, all_input_names, all_output_names)
455
- ):
456
- raise ClassiqControlError()
457
- return control_states
458
-
459
326
  @staticmethod
460
327
  def _has_repetitions(name_list: Sequence[str]) -> bool:
461
328
  return len(set(name_list)) < len(name_list)
462
329
 
463
- @staticmethod
464
- def _validate_slices(
465
- io: PortDirection,
466
- input_names: Collection[str],
467
- fp: FunctionParams,
468
- strict_zero_ios: bool,
469
- control_states: List[ControlState],
470
- ) -> None:
471
- name_slice_pairs = [parse_io_slicing(input) for input in input_names]
472
- slices_dict: Dict[str, List[slice]] = defaultdict(list)
473
- for name, slice_obj in name_slice_pairs:
474
- slices_dict[name].append(slice_obj)
475
-
476
- fp_inputs = (
477
- fp.inputs_full(strict_zero_ios)
478
- if (io == PortDirection.Input)
479
- else fp.outputs
480
- )
481
- widths = {name: reg.size for name, reg in fp_inputs.items()}
482
- control_names = {state.name for state in control_states}
483
-
484
- for name in slices_dict:
485
- if name in control_names:
486
- continue
487
- assert name in widths, "Name not in widths"
488
- if not QuantumFunctionCall._register_validate_slices(
489
- slices_dict[name], widths[name]
490
- ):
491
- raise ValueError(BAD_INPUT_SLICING_MSG)
492
-
493
- @staticmethod
494
- def _register_validate_slices(slices: List[slice], reg_width: int) -> bool:
495
- widths_separated = [len(range(reg_width)[reg_slice]) for reg_slice in slices]
496
- # examples: slice(0), slice(5,None) when width <= 5, slice(5,3)
497
- empty_slices = 0 in widths_separated
498
-
499
- max_stop = max(reg_slice.stop or 0 for reg_slice in slices)
500
- out_of_range = max_stop > reg_width
501
-
502
- all_widths_separated = sum(widths_separated)
503
- all_indices = set(
504
- itertools.chain.from_iterable(
505
- range(reg_width)[reg_slice] for reg_slice in slices
506
- )
507
- )
508
- all_widths_combined = len(all_indices)
509
- overlapping_slices = all_widths_combined != all_widths_separated
510
-
511
- return not any((empty_slices, out_of_range, overlapping_slices))
512
-
513
- @pydantic.validator("inputs")
514
- def _validate_inputs(
515
- cls, inputs: Mapping[str, HandleBinding], values: Dict[str, Any]
516
- ) -> Mapping[str, HandleBinding]:
517
- params: Optional[FunctionParams] = values.get("function_params")
518
- strict_zero_ios: bool = values.get("strict_zero_ios", True)
519
- control_states: List[ControlState] = values.get("control_states", list())
520
- if params is None:
521
- return dict()
522
- if isinstance(params, CustomFunction):
523
- if not isinstance(inputs, dict):
524
- raise ValueError(CUSTOM_FUNCTION_SINGLE_IO_ERROR)
525
- return inputs
526
-
527
- cls._validate_input_names(
528
- params=params,
529
- input_names=inputs.keys(),
530
- control_states=control_states,
531
- strict_zero_ios=strict_zero_ios,
532
- )
533
-
534
- cls._validate_slices(
535
- PortDirection.Input,
536
- inputs.keys(),
537
- params,
538
- strict_zero_ios,
539
- control_states,
540
- )
541
-
542
- return inputs
543
-
544
- @staticmethod
545
- def _validate_output_names(
546
- *,
547
- params: f_params.FunctionParams,
548
- output_names: Collection[str],
549
- control_states: List[ControlState],
550
- strict_zero_ios: bool,
551
- ) -> None:
552
- (
553
- invalid_expressions,
554
- invalid_slicings,
555
- invalid_names,
556
- ) = QuantumFunctionCall._get_invalid_ios(
557
- expressions=output_names,
558
- params=params,
559
- io=PortDirection.Output,
560
- control_states=control_states,
561
- strict_zero_ios=strict_zero_ios,
562
- )
563
- error_msg = []
564
- if invalid_expressions:
565
- error_msg.append(f"{BAD_OUTPUT_EXPRESSION_MSG}: {invalid_expressions}")
566
- if invalid_names:
567
- error_msg.append(f"{BAD_OUTPUT_ERROR_MSG}: {invalid_names}")
568
- if invalid_slicings:
569
- error_msg.append(f"{BAD_OUTPUT_SLICING_MSG}: {invalid_slicings}")
570
- if error_msg:
571
- raise ValueError("\n".join(error_msg))
572
-
573
- @pydantic.validator("outputs")
574
- def _validate_outputs(
575
- cls, outputs: Mapping[str, HandleBinding], values: Dict[str, Any]
576
- ) -> Mapping[str, HandleBinding]:
577
- params = values.get("function_params")
578
- strict_zero_ios: bool = values.get("strict_zero_ios", True)
579
- control_states = values.get("control_states", list())
580
- if params is None:
581
- return outputs
582
- if isinstance(params, CustomFunction):
583
- if not isinstance(outputs, dict):
584
- raise ValueError(CUSTOM_FUNCTION_SINGLE_IO_ERROR)
585
- return outputs
586
-
587
- cls._validate_output_names(
588
- params=params,
589
- output_names=outputs.keys(),
590
- control_states=control_states,
591
- strict_zero_ios=strict_zero_ios,
592
- )
593
-
594
- cls._validate_slices(
595
- PortDirection.Output,
596
- outputs.keys(),
597
- params,
598
- strict_zero_ios,
599
- control_states,
600
- )
601
-
602
- return outputs
603
-
604
- @staticmethod
605
- def _get_invalid_ios(
606
- *,
607
- expressions: Iterable[str],
608
- params: f_params.FunctionParams,
609
- io: f_params.PortDirection,
610
- control_states: List[ControlState],
611
- strict_zero_ios: bool,
612
- ) -> Tuple[List[str], List[str], List[str]]:
613
- expression_matches: Iterable[Optional[Match]] = map(
614
- functools.partial(re.fullmatch, IO_REGEX), expressions
615
- )
616
-
617
- valid_matches: List[Match] = []
618
- invalid_expressions: List[str] = []
619
- for expression, expression_match in zip(expressions, expression_matches):
620
- invalid_expressions.append(
621
- expression
622
- ) if expression_match is None else valid_matches.append(expression_match)
623
-
624
- invalid_slicings: List[str] = []
625
- invalid_names: List[str] = []
626
- valid_names = frozenset(
627
- params.inputs_full(strict_zero_ios)
628
- if io == PortDirection.Input
629
- else params.outputs
630
- )
631
- for match in valid_matches:
632
- name = match.groupdict().get(NAME)
633
- if name is None:
634
- raise AssertionError("Input/output name validation error")
635
-
636
- slicing = match.groupdict().get(SLICING)
637
- if slicing is not None and re.fullmatch(LEGAL_SLICING, slicing) is None:
638
- invalid_slicings.append(match.string)
639
-
640
- if name in valid_names:
641
- continue
642
- elif all(state.name != name for state in control_states):
643
- invalid_names.append(name)
644
-
645
- return invalid_expressions, invalid_slicings, invalid_names
646
-
647
- def get_param_exprs(self) -> Dict[str, Expression]:
648
- if isinstance(self.function_params, CustomFunction):
649
- return self.params
650
- else:
651
- return {
652
- name: Expression(expr=raw_expr)
653
- for name, raw_expr in self.function_params
654
- if self.function_params.is_field_gen_param(name)
655
- }
656
-
657
330
  @pydantic.root_validator()
658
331
  def validate_handles(cls, values: Dict[str, Any]) -> Dict[str, Any]:
659
332
  inputs = values.get("inputs", dict())
@@ -666,9 +339,6 @@ class QuantumFunctionCall(QuantumOperation):
666
339
 
667
340
  return values
668
341
 
669
- class Config:
670
- extra = Extra.forbid
671
-
672
342
 
673
343
  class QuantumLambdaFunction(BaseModel):
674
344
  """
@@ -1,6 +1,6 @@
1
1
  from typing import Mapping, Union
2
2
 
3
- from pydantic import BaseModel
3
+ from pydantic import BaseModel, Extra
4
4
 
5
5
  from classiq.interface.model.handle_binding import (
6
6
  HandleBinding,
@@ -10,7 +10,8 @@ from classiq.interface.model.handle_binding import (
10
10
 
11
11
 
12
12
  class QuantumStatement(BaseModel):
13
- pass
13
+ class Config:
14
+ extra = Extra.forbid
14
15
 
15
16
 
16
17
  class QuantumOperation(QuantumStatement):
@@ -3,6 +3,8 @@ CHEMISTRY_PREFIX = "/chemistry"
3
3
  LEGACY_EXECUTE_PREFIX = "/execute"
4
4
  EXECUTION_PREFIX = "/execution"
5
5
 
6
+ SYNTHESIS_SERVICE_PREFIX = "/synthesis/v1"
7
+
6
8
  ANALYZER_CIRCUIT_PAGE = "circuit"
7
9
  DEFAULT_IDE_FE_APP = "https://platform.classiq.io/"
8
10
 
@@ -34,9 +36,7 @@ DATA_DOG_EVENT_TASK_FULL_PATH = f"{ANALYZER_PREFIX}{DATA_DOG_EVENT_TASK}"
34
36
 
35
37
  IDE_QASM_TASK = f"{TASKS_SUFFIX}/generated_circuit_from_qasm"
36
38
  IDE_QASM_FULL_PATH = f"{ANALYZER_PREFIX}{IDE_QASM_TASK}"
37
- IDE_EXECUTION_RESULTS_PATH = f"/results{LEGACY_EXECUTE_PREFIX}"
38
39
  TASKS_GENERATE_SUFFIX = TASKS_SUFFIX + "/generate"
39
- SEMANTICS_CHECK_SUFFIX = TASKS_SUFFIX + "/semantic_checks"
40
40
  TASKS_VISUALIZE_SUFFIX = TASKS_SUFFIX + "/visualize"
41
41
  TASKS_SOLVE_SUFFIX = "/tasks/solve"
42
42
  MODEL_GENERATE_PREFIX = "/generate_model"
@@ -50,7 +50,6 @@ TASKS_GENERATE_FULL_PATH = TASKS_GENERATE_SUFFIX
50
50
  TASKS_SOLVE_EXACT_SUFFIX = "/tasks/solve_exact"
51
51
  TASKS_UCC_OPERATORS_SUFFIX = "/tasks/ucc_operators"
52
52
 
53
- TASKS_GET_MAX_CUT_MODEL = "/tasks/get_max_cut_model"
54
53
 
55
54
  EXECUTION_JOBS_SUFFIX = "/jobs"
56
55
  EXECUTION_JOBS_FULL_PATH = EXECUTION_PREFIX + EXECUTION_JOBS_SUFFIX
@@ -61,12 +60,12 @@ ANALYZER_FULL_PATH = ANALYZER_PREFIX + TASKS_SUFFIX
61
60
  ANALYZER_RB_FULL_PATH = ANALYZER_PREFIX + TASK_RB_SUFFIX
62
61
  GENERATE_RESOURCE_ESTIMATOR_REPORT = "/resource_estimator_report"
63
62
 
64
- CHEMISTRY_GENERATE_HAMILTONIAN_FULL_PATH = CHEMISTRY_PREFIX + TASKS_SUFFIX
63
+ GENERATE_HAMILTONIAN_SUFFIX = "/generate_hamiltonian"
64
+ GENERATE_HAMILTONIAN_FULL_PATH = SYNTHESIS_SERVICE_PREFIX + GENERATE_HAMILTONIAN_SUFFIX
65
+
65
66
  CHEMISTRY_GENERATE_UCC_OPERATORS_FULL_PATH = (
66
67
  CHEMISTRY_PREFIX + TASKS_UCC_OPERATORS_SUFFIX
67
68
  )
68
- CHEMISTRY_SOLVE_EXACT_FULL_PATH = CHEMISTRY_PREFIX + TASKS_SOLVE_EXACT_SUFFIX
69
- CHEMISTRY_SOLVE_FULL_PATH = CHEMISTRY_PREFIX + TASKS_SOLVE_SUFFIX
70
69
 
71
70
  FINANCE_GENERATE_MODEL_PATH = MODEL_GENERATE_PREFIX + "/finance"
72
71