What's New in Version 1.0.19
Version 1.0.19 of the relationalai Python package is now available!
To upgrade, activate your virtual environment and run the following command:
pip install --upgrade relationalaiNew Features and Enhancements
Section titled “New Features and Enhancements”Graph.is_acyclic()is now much faster on many large graphs, especially large trees and grids. In benchmarks for the change, the runtime for the largest tested grid graph dropped from about 751 seconds to 47 seconds.
Bug Fixes
Section titled “Bug Fixes”-
Removed unsupported
std.reAPIs. These APIs would raise errors if you tried to use them. All documentation pertaining tostd.rehas also been removed. -
Fixed active profile selection so that the
RAI_ACTIVE_PROFILEenvironment variable takes precedence overactive_profileinraiconfig.yaml. -
Fixed prescriptive models that combined filtered aggregates in one expression. Before, PyRel could leak one aggregate’s filter into another, so objectives and constraints could silently drop instead of being sent to the solver. For example:
from relationalai.semantics import Float, Integer, Modelfrom relationalai.semantics.reasoners.prescriptive import Problemmodel = Model("ScopedAggregates")X = model.Concept("X", identify_by={"i": Integer})X.v = model.Property(f"{X} has {Float:v}")model.define(X.new(i=1), X.new(i=2))problem = Problem(model, Float)problem.solve_for(X.v, name=["v", X.i], lower=0, upper=10)v = Float.ref()problem.satisfy(model.require(sum(v).where(X.v(v), X.i == 1) == 1.0))problem.satisfy(model.require(sum(v).where(X.v(v), X.i == 2) == 4.0))# The bug was triggered by combining two filtered aggregates in one expression.problem.minimize(sum(X.v).where(X.i == 1) + sum(X.v).where(X.i == 2))problem.satisfy(model.require(sum(X.v).where(X.i == 1) <= sum(X.v).where(X.i == 2)))problem.solve("highs")print(model.select(problem.num_min_objectives().alias("objective_count")).to_df())print(model.select(problem.num_constraints().alias("constraint_count")).to_df())print("objective_value:", problem.solve_info().objective_value)Output before the fix:
objective_count0 0constraint_count0 2objective_value: 0.0Output after the fix:
objective_count0 1constraint_count0 3objective_value: 5.0 -
Fixed
model.data()calls created on the same source line, such as inside a loop.model.data()creates a temporary data source each time you call it. Before, if two calls happened on the same line, PyRel could mistake them for the same source. That could make later queries fail even when the input data was valid.For example:
import pandas as pdfrom relationalai.semantics import Integer, Model, Stringm = Model("DataLoop")Item = m.Concept("Item", identify_by={"id": Integer, "name": String})batches = [pd.DataFrame([(1, "a"), (2, "b")], columns=["id", "name"]),pd.DataFrame([(3, "c"), (4, "d")], columns=["id", "name"]),]for batch in batches:m.define(Item.new(m.data(batch).to_schema()))print(m.select(Item.id, Item.name).to_df())Output before the fix:
RelQueryError: Query errorOutput after the fix:
id name0 1 a1 2 b2 3 c3 4 dNow PyRel gives each same-line data source its own deterministic sequence number, so queries compile and run correctly.